whirr-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From adrianc...@apache.org
Subject svn commit: r1172926 [1/2] - in /whirr/trunk: ./ cli/ core/src/main/java/org/apache/whirr/ core/src/main/java/org/apache/whirr/actions/ core/src/main/java/org/apache/whirr/service/ core/src/test/java/org/apache/whirr/ core/src/test/java/org/apache/whir...
Date Tue, 20 Sep 2011 00:44:24 GMT
Author: adriancole
Date: Tue Sep 20 00:44:22 2011
New Revision: 1172926

URL: http://svn.apache.org/viewvc?rev=1172926&view=rev
Log:
WHIRR-385: masterless puppet service support

Added:
    whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterActionHandlerFactory.java
    whirr/trunk/recipes/puppet-http-ec2.properties
    whirr/trunk/recipes/puppet-http-rackspace.properties
    whirr/trunk/services/puppet/
    whirr/trunk/services/puppet/pom.xml
    whirr/trunk/services/puppet/src/
    whirr/trunk/services/puppet/src/main/
    whirr/trunk/services/puppet/src/main/java/
    whirr/trunk/services/puppet/src/main/java/org/
    whirr/trunk/services/puppet/src/main/java/org/apache/
    whirr/trunk/services/puppet/src/main/java/org/apache/whirr/
    whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/
    whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/
    whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/Manifest.java
    whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/PuppetClusterActionHandler.java
    whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/PuppetClusterActionHandlerFactory.java
    whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/PuppetConstants.java
    whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/PuppetInstallClusterActionHandler.java
    whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/
    whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/InstallAllModulesStatementFromProperties.java
    whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/KeyToModuleNameOrNull.java
    whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/ModulePropertiesFromConfiguration.java
    whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/RolesManagedByPuppet.java
    whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/StatementToInstallModule.java
    whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/predicates/
    whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/predicates/PuppetPredicates.java
    whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/statements/
    whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/statements/CreateSitePpAndApplyRoles.java
    whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/statements/InstallModuleFromGit.java
    whirr/trunk/services/puppet/src/main/resources/
    whirr/trunk/services/puppet/src/main/resources/META-INF/
    whirr/trunk/services/puppet/src/main/resources/META-INF/services/
    whirr/trunk/services/puppet/src/main/resources/META-INF/services/org.apache.whirr.service.ClusterActionHandler
    whirr/trunk/services/puppet/src/main/resources/META-INF/services/org.apache.whirr.service.ClusterActionHandlerFactory
    whirr/trunk/services/puppet/src/main/resources/functions/
    whirr/trunk/services/puppet/src/main/resources/functions/install_puppet.sh
    whirr/trunk/services/puppet/src/main/resources/functions/install_ruby.sh
    whirr/trunk/services/puppet/src/test/
    whirr/trunk/services/puppet/src/test/java/
    whirr/trunk/services/puppet/src/test/java/org/
    whirr/trunk/services/puppet/src/test/java/org/apache/
    whirr/trunk/services/puppet/src/test/java/org/apache/whirr/
    whirr/trunk/services/puppet/src/test/java/org/apache/whirr/service/
    whirr/trunk/services/puppet/src/test/java/org/apache/whirr/service/puppet/
    whirr/trunk/services/puppet/src/test/java/org/apache/whirr/service/puppet/ManifestTest.java
    whirr/trunk/services/puppet/src/test/java/org/apache/whirr/service/puppet/PuppetClusterActionHandlerFactoryTest.java
    whirr/trunk/services/puppet/src/test/java/org/apache/whirr/service/puppet/functions/
    whirr/trunk/services/puppet/src/test/java/org/apache/whirr/service/puppet/functions/InstallAllModulesStatementFromPropertiesTest.java
    whirr/trunk/services/puppet/src/test/java/org/apache/whirr/service/puppet/functions/KeyToModuleNameOrNullTest.java
    whirr/trunk/services/puppet/src/test/java/org/apache/whirr/service/puppet/functions/ModulePropertiesFromConfigurationTest.java
    whirr/trunk/services/puppet/src/test/java/org/apache/whirr/service/puppet/functions/RolesManagedByPuppetTest.java
    whirr/trunk/services/puppet/src/test/java/org/apache/whirr/service/puppet/functions/StatementToInstallModuleTest.java
    whirr/trunk/services/puppet/src/test/java/org/apache/whirr/service/puppet/integration/
    whirr/trunk/services/puppet/src/test/java/org/apache/whirr/service/puppet/integration/PuppetServiceTest.java
    whirr/trunk/services/puppet/src/test/java/org/apache/whirr/service/puppet/predicates/
    whirr/trunk/services/puppet/src/test/java/org/apache/whirr/service/puppet/predicates/PuppetPredicatesTest.java
    whirr/trunk/services/puppet/src/test/java/org/apache/whirr/service/puppet/statements/
    whirr/trunk/services/puppet/src/test/java/org/apache/whirr/service/puppet/statements/CreateSitePpAndApplyRolesTest.java
    whirr/trunk/services/puppet/src/test/java/org/apache/whirr/service/puppet/statements/InstallModuleFromGitTest.java
    whirr/trunk/services/puppet/src/test/resources/
    whirr/trunk/services/puppet/src/test/resources/log4j.xml
    whirr/trunk/services/puppet/src/test/resources/nginx-with-attribs.txt
    whirr/trunk/services/puppet/src/test/resources/whirr-puppet-test.properties
Modified:
    whirr/trunk/cli/pom.xml
    whirr/trunk/core/src/main/java/org/apache/whirr/HandlerMapFactory.java
    whirr/trunk/core/src/main/java/org/apache/whirr/actions/ScriptBasedClusterAction.java
    whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterActionEvent.java
    whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterActionHandler.java
    whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterActionHandlerSupport.java
    whirr/trunk/core/src/test/java/org/apache/whirr/ClusterSpecTest.java
    whirr/trunk/core/src/test/java/org/apache/whirr/actions/BootstrapClusterActionTest.java
    whirr/trunk/pom.xml

Modified: whirr/trunk/cli/pom.xml
URL: http://svn.apache.org/viewvc/whirr/trunk/cli/pom.xml?rev=1172926&r1=1172925&r2=1172926&view=diff
==============================================================================
--- whirr/trunk/cli/pom.xml (original)
+++ whirr/trunk/cli/pom.xml Tue Sep 20 00:44:22 2011
@@ -80,6 +80,11 @@
       <version>${project.version}</version>
     </dependency>
     <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>whirr-puppet</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
     </dependency>

Modified: whirr/trunk/core/src/main/java/org/apache/whirr/HandlerMapFactory.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/HandlerMapFactory.java?rev=1172926&r1=1172925&r2=1172926&view=diff
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/HandlerMapFactory.java (original)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/HandlerMapFactory.java Tue Sep 20 00:44:22 2011
@@ -18,34 +18,84 @@
 
 package org.apache.whirr;
 
-import com.google.common.collect.Maps;
+import static com.google.common.base.Preconditions.checkNotNull;
 
 import java.util.Map;
 import java.util.ServiceLoader;
 
 import org.apache.whirr.service.ClusterActionHandler;
+import org.apache.whirr.service.ClusterActionHandlerFactory;
+
+import com.google.common.base.Function;
+import com.google.common.collect.MapMaker;
+import com.google.common.collect.Maps;
 
 /**
- * HandlerMapFactory used in ScriptBasedClusterAction classes to 
- * create a map of action handlers.
- * This implementation allow reuse of the same handler map by 
- * multiple ScriptBasedClusterAction instances.
+ * HandlerMapFactory used in ScriptBasedClusterAction classes to create a map of action handlers.
+ * This implementation allow reuse of the same handler map by multiple ScriptBasedClusterAction
+ * instances.
  * 
- * Note: Another motivation of extracting this code outside of ScriptBasedClusterAction
- * is that the same code inside action implementation is not possible to mock by Mockito
- * for JUnit testing purposes. Without this code the action classes can be plugged with
- * another factory and remain JUnit testable.
+ * Note: Another motivation of extracting this code outside of ScriptBasedClusterAction is that the
+ * same code inside action implementation is not possible to mock by Mockito for JUnit testing
+ * purposes. Without this code the action classes can be plugged with another factory and remain
+ * JUnit testable.
  */
-class HandlerMapFactory {
+public class HandlerMapFactory {
+
+   protected static final class ReturnHandlerByRoleOrPrefix implements Function<String, ClusterActionHandler> {
+      private final Map<String, ClusterActionHandlerFactory> factoryMap;
+      private final Map<String, ClusterActionHandler> handlerMap;
+
+      protected ReturnHandlerByRoleOrPrefix(Map<String, ClusterActionHandlerFactory> factoryMap,
+               Map<String, ClusterActionHandler> handlerMap) {
+         this.factoryMap = checkNotNull(factoryMap, "factoryMap");
+         this.handlerMap = checkNotNull(handlerMap, "handlerMap");
+      }
+
+      @Override
+      public ClusterActionHandler apply(String arg0) {
+         checkNotNull(arg0, "role");
+         for (String prefix : factoryMap.keySet()) {
+            if (arg0.startsWith(prefix))
+               return factoryMap.get(prefix).create(arg0.substring(prefix.length()));
+         }
+         return handlerMap.get(arg0);
+      }
+   }
+
+   public static Map<String, ClusterActionHandler> create() {
+      return create(ServiceLoader.load(ClusterActionHandlerFactory.class), ServiceLoader
+               .load(ClusterActionHandler.class));
+   }
+
+   public static Map<String, ClusterActionHandler> create(Iterable<ClusterActionHandlerFactory> factories,
+            Iterable<ClusterActionHandler> handlers) {
+      Map<String, ClusterActionHandlerFactory> factoryMap = indexFactoriesByRolePrefix(checkNotNull(factories,
+               "factories"));
+      Map<String, ClusterActionHandler> handlerMap = indexHandlersByRole(checkNotNull(handlers, "handlers"));
+      return new MapMaker().makeComputingMap(new ReturnHandlerByRoleOrPrefix(factoryMap, handlerMap));
+   }
+
+   static Map<String, ClusterActionHandlerFactory> indexFactoriesByRolePrefix(
+            Iterable<ClusterActionHandlerFactory> handlers) {
+      return Maps.uniqueIndex(handlers, new Function<ClusterActionHandlerFactory, String>() {
+
+         @Override
+         public String apply(ClusterActionHandlerFactory arg0) {
+            return arg0.getRolePrefix();
+         }
+
+      });
+   }
+
+   static Map<String, ClusterActionHandler> indexHandlersByRole(Iterable<ClusterActionHandler> handlers) {
+      return Maps.uniqueIndex(handlers, new Function<ClusterActionHandler, String>() {
+
+         @Override
+         public String apply(ClusterActionHandler arg0) {
+            return arg0.getRole();
+         }
 
-  private ServiceLoader<ClusterActionHandler> clusterActionHandlerLoader =
-    ServiceLoader.load(ClusterActionHandler.class);
-  
-  Map<String, ClusterActionHandler> create() {
-    Map<String, ClusterActionHandler> handlerMap = Maps.newHashMap();
-    for (ClusterActionHandler handler : clusterActionHandlerLoader) {
-      handlerMap.put(handler.getRole(), handler);
-    }
-    return handlerMap;
-  }
+      });
+   }
 }

Modified: whirr/trunk/core/src/main/java/org/apache/whirr/actions/ScriptBasedClusterAction.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/actions/ScriptBasedClusterAction.java?rev=1172926&r1=1172925&r2=1172926&view=diff
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/actions/ScriptBasedClusterAction.java (original)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/actions/ScriptBasedClusterAction.java Tue Sep 20 00:44:22 2011
@@ -64,15 +64,15 @@ public abstract class ScriptBasedCluster
           clusterSpec, newCluster);
 
       ClusterActionEvent event = new ClusterActionEvent(getAction(),
-          clusterSpec, newCluster, statementBuilder, getCompute(), firewallManager);
+          clusterSpec, instanceTemplate, newCluster, statementBuilder, getCompute(), firewallManager);
 
       eventMap.put(instanceTemplate, event);
       for (String role : instanceTemplate.getRoles()) {
-        ClusterActionHandler handler = handlerMap.get(role);
-        if (handler == null) {
+        try {
+          handlerMap.get(role).beforeAction(event);
+        } catch (NullPointerException e) {
           throw new IllegalArgumentException("No handler for role " + role);
         }
-        handler.beforeAction(event);
       }
       newCluster = event.getCluster(); // cluster may have been updated by handler 
     }
@@ -84,13 +84,13 @@ public abstract class ScriptBasedCluster
 
     for (InstanceTemplate instanceTemplate : clusterSpec.getInstanceTemplates()) {
       for (String role : instanceTemplate.getRoles()) {
-        ClusterActionHandler handler = handlerMap.get(role);
-        if (handler == null) {
-          throw new IllegalArgumentException("No handler for role " + role);
-        }
         ClusterActionEvent event = eventMap.get(instanceTemplate);
         event.setCluster(newCluster);
-        handler.afterAction(event);
+        try {
+          handlerMap.get(role).afterAction(event);
+        } catch (NullPointerException e) {
+          throw new IllegalArgumentException("No handler for role " + role);
+        }
         newCluster = event.getCluster(); // cluster may have been updated by handler 
       }
     }

Modified: whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterActionEvent.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterActionEvent.java?rev=1172926&r1=1172925&r2=1172926&view=diff
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterActionEvent.java (original)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterActionEvent.java Tue Sep 20 00:44:22 2011
@@ -20,6 +20,7 @@ package org.apache.whirr.service;
 
 import org.apache.whirr.Cluster;
 import org.apache.whirr.ClusterSpec;
+import org.apache.whirr.InstanceTemplate;
 import org.apache.whirr.service.jclouds.StatementBuilder;
 import org.apache.whirr.service.jclouds.TemplateBuilderStrategy;
 import org.jclouds.compute.ComputeServiceContext;
@@ -33,6 +34,7 @@ public class ClusterActionEvent {
   
   private String action;
   private ClusterSpec clusterSpec;
+  private InstanceTemplate instanceTemplate;
   private Cluster cluster;
   private StatementBuilder statementBuilder;
   private TemplateBuilderStrategy templateBuilderStrategy =
@@ -41,17 +43,19 @@ public class ClusterActionEvent {
   private Function<ClusterSpec, ComputeServiceContext> getCompute;
   
   public ClusterActionEvent(String action, ClusterSpec clusterSpec,
-      Cluster cluster, Function<ClusterSpec, ComputeServiceContext> getCompute,
+      InstanceTemplate instanceTemplate, Cluster cluster, 
+      Function<ClusterSpec, ComputeServiceContext> getCompute,
       FirewallManager firewallManager) {
-    this(action, clusterSpec, cluster, null, getCompute, firewallManager);
+    this(action, clusterSpec, instanceTemplate, cluster, null, getCompute, firewallManager);
   }
   
   public ClusterActionEvent(String action, ClusterSpec clusterSpec,
-      Cluster cluster, StatementBuilder statementBuilder,
+      InstanceTemplate instanceTemplate, Cluster cluster, StatementBuilder statementBuilder,
       Function<ClusterSpec, ComputeServiceContext> getCompute,
       FirewallManager firewallManager) {
     this.action = action;
     this.clusterSpec = clusterSpec;
+    this.instanceTemplate = instanceTemplate;
     this.cluster = cluster;
     this.statementBuilder = statementBuilder;
     this.getCompute = getCompute;
@@ -74,6 +78,10 @@ public class ClusterActionEvent {
     return clusterSpec;
   }
   
+  public InstanceTemplate getInstanceTemplate() {
+    return instanceTemplate;
+  }
+  
   public Function<ClusterSpec, ComputeServiceContext> getCompute() {
     return getCompute;
   }

Modified: whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterActionHandler.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterActionHandler.java?rev=1172926&r1=1172925&r2=1172926&view=diff
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterActionHandler.java (original)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterActionHandler.java Tue Sep 20 00:44:22 2011
@@ -20,6 +20,8 @@ package org.apache.whirr.service;
 
 import java.io.IOException;
 
+import com.google.common.base.Objects;
+
 /**
  * A callback interface for cluster actions that apply to instances in a
  * given role.
@@ -53,4 +55,26 @@ public abstract class ClusterActionHandl
   public void afterAction(ClusterActionEvent event)
       throws IOException, InterruptedException {
   }
+
+  /**
+   * this uses the inefficient {@link Objects} implementation as the object count will be
+   * relatively small and therefore efficiency is not a concern.
+   */
+  @Override
+  public int hashCode() {
+     return Objects.hashCode(getRole());
+  }
+
+  @Override
+  public boolean equals(Object that) {
+     if (that == null)
+        return false;
+     return Objects.equal(this.toString(), that.toString());
+  }
+
+  @Override
+  public String toString() {
+     return Objects.toStringHelper(this).add("role", getRole()).toString();
+  }
+
 }

Added: whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterActionHandlerFactory.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterActionHandlerFactory.java?rev=1172926&view=auto
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterActionHandlerFactory.java (added)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterActionHandlerFactory.java Tue Sep 20 00:44:22 2011
@@ -0,0 +1,38 @@
+/**
+ * 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;
+
+
+/**
+ * Produces {@link ClusterActionHandler}s
+ * <p>
+ * <i>Implementation note.</i> {@link ClusterActionHandlerFactory} implementations are
+ * discovered using a Service Provider Interface (SPI), described in
+ * {@link java.util.ServiceLoader}.
+ */
+public abstract class ClusterActionHandlerFactory {
+
+  /**
+   * @return prefix that should use this factory
+   */
+  public abstract String getRolePrefix();
+
+  public abstract ClusterActionHandler create(String roleName);
+
+}

Modified: whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterActionHandlerSupport.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterActionHandlerSupport.java?rev=1172926&r1=1172925&r2=1172926&view=diff
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterActionHandlerSupport.java (original)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterActionHandlerSupport.java Tue Sep 20 00:44:22 2011
@@ -147,7 +147,7 @@ public abstract class ClusterActionHandl
    * @param rawUrl    raw url as provided in the configuration file
    * @return  an URL visible to the install / configure scripts
    */
-  public String prepareRemoteFileUrl(ClusterActionEvent event, String rawUrl)
+  public static String prepareRemoteFileUrl(ClusterActionEvent event, String rawUrl)
       throws IOException {
     if (rawUrl != null && rawUrl.startsWith("file://")) {
       try {

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=1172926&r1=1172925&r2=1172926&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 Sep 20 00:44:22 2011
@@ -310,6 +310,24 @@ public class ClusterSpecTest {
       assertThat(spec.getBlobStoreProvider(), is(parts[1]));
     }
   }
+  
+  @Test
+  public void testApplySubroleAliases() {
+    CompositeConfiguration c = new CompositeConfiguration();
+    Configuration config = new PropertiesConfiguration();
+    config.addProperty("whirr.instance-templates", 
+        "1 puppet:somepup::pet+something-else, 1 something-else-only");
+    c.addConfiguration(config);    
+    InstanceTemplate template = InstanceTemplate.parse(c).get(0);
+    Set<String> expected = Sets.newLinkedHashSet(Arrays.asList(new String[]{
+        "puppet:somepup::pet", "something-else"}));
+    assertThat(template.getRoles(), is(expected));
+    
+    InstanceTemplate template2 = InstanceTemplate.parse(c).get(1);
+    Set<String> expected2 = Sets.newLinkedHashSet(Arrays.asList(new String[]{
+        "something-else-only"}));
+    assertThat(template2.getRoles(), is(expected2));
+  }
 
   @Test
   public void testCopySpec() throws Exception {

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=1172926&r1=1172925&r2=1172926&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 Sep 20 00:44:22 2011
@@ -43,7 +43,9 @@ import org.apache.commons.configuration.
 import org.apache.commons.configuration.Configuration;
 import org.apache.commons.configuration.PropertiesConfiguration;
 import org.apache.whirr.ClusterSpec;
+import org.apache.whirr.HandlerMapFactory;
 import org.apache.whirr.service.ClusterActionHandler;
+import org.apache.whirr.service.ClusterActionHandlerFactory;
 import org.jclouds.compute.ComputeService;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.RunNodesException;
@@ -316,4 +318,122 @@ public class BootstrapClusterActionTest 
       return nodes;
     }
   }
+  
+  @Test
+  @SuppressWarnings("unchecked")
+  public void testSubroleInvoked() throws Exception {
+    CompositeConfiguration config = new CompositeConfiguration();
+    if (System.getProperty("config") != null) {
+      config.addConfiguration(new PropertiesConfiguration(System.getProperty("config")));
+    }
+    Configuration conf = new PropertiesConfiguration();
+    conf.addProperty("whirr.service-name", "test-service");
+    conf.addProperty("whirr.cluster-name", "test-cluster");
+    conf.addProperty("whirr.instance-templates", "1 puppet:module::manifest+something-else");
+    conf.addProperty("whirr.provider", "ec2");
+    config.addConfiguration(conf);
+    ClusterSpec clusterSpec = ClusterSpec.withTemporaryKeys(conf);
+
+    Set<String> nn = new HashSet<String>();
+    nn.add("puppet:module::manifest"); 
+    nn.add("something-else");     
+
+    TestNodeStarterFactory nodeStarterFactory = null;
+    
+    ClusterActionHandlerFactory puppetHandlerFactory = mock(ClusterActionHandlerFactory.class);
+    ClusterActionHandler handler = mock(ClusterActionHandler.class);
+    when(puppetHandlerFactory.getRolePrefix()).thenReturn("puppet:");
+    when(puppetHandlerFactory.create("module::manifest")).thenReturn(handler);
+    when(handler.getRole()).thenReturn("something-else");
+
+    Map<String, ClusterActionHandler> handlerMap = HandlerMapFactory.create(ImmutableSet.of(puppetHandlerFactory),
+          ImmutableSet.of(handler));
+
+    Function<ClusterSpec, ComputeServiceContext> getCompute = mock(Function.class);
+    ComputeServiceContext serviceContext = mock(ComputeServiceContext.class);
+    ComputeService computeService = mock(ComputeService.class);
+    TemplateBuilder templateBuilder = mock(TemplateBuilder.class);
+    Template template = mock(Template.class);
+
+    when(getCompute.apply(clusterSpec)).thenReturn(serviceContext);
+    when(serviceContext.getComputeService()).thenReturn(computeService);
+    when(computeService.templateBuilder()).thenReturn(templateBuilder);
+    when(templateBuilder.options((TemplateOptions) any())).thenReturn(templateBuilder);
+    when(templateBuilder.build()).thenReturn(template);
+    
+    Map<Set<String>, Stack<Integer>> reaction = Maps.newHashMap();
+    Stack<Integer> nnStack = new Stack<Integer>();
+    nnStack.push(new Integer(1));
+    reaction.put(nn, nnStack);
+    
+    nodeStarterFactory = new TestNodeStarterFactory(reaction);
+    BootstrapClusterAction bootstrapper = new BootstrapClusterAction(getCompute, handlerMap, nodeStarterFactory);
+    
+    bootstrapper.execute(clusterSpec, null);
+    
+    if (nodeStarterFactory != null) {
+      nodeStarterFactory.validateCompletion();
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  @Test(expected = IllegalArgumentException.class)
+  /** test is the same as previous (SubroleInvoked) except it knows puppet, not puppet:, as the role;
+   * the colon in the role def'n is the indication it accepts subroles,
+   * so this should throw IllegalArgument when we refer to puppet:module...
+   */
+  public void testSubroleNotSupported() throws Exception {
+    CompositeConfiguration config = new CompositeConfiguration();
+    if (System.getProperty("config") != null) {
+      config.addConfiguration(new PropertiesConfiguration(System.getProperty("config")));
+    }
+    Configuration conf = new PropertiesConfiguration();
+    conf.addProperty("whirr.service-name", "test-service");
+    conf.addProperty("whirr.cluster-name", "test-cluster");
+    conf.addProperty("whirr.instance-templates", "1 puppet:module::manifest+something-else");
+    conf.addProperty("whirr.provider", "ec2");
+    config.addConfiguration(conf);
+    ClusterSpec clusterSpec = ClusterSpec.withTemporaryKeys(conf);
+
+    Set<String> nn = new HashSet<String>();
+    nn.add("puppet:module::manifest"); 
+    nn.add("something-else");     
+
+    TestNodeStarterFactory nodeStarterFactory = null;
+    
+    ClusterActionHandlerFactory puppetHandlerFactory = mock(ClusterActionHandlerFactory.class);
+    ClusterActionHandler handler = mock(ClusterActionHandler.class);
+    when(puppetHandlerFactory.getRolePrefix()).thenReturn("puppet");
+    when(handler.getRole()).thenReturn("something-else");
+
+    Map<String, ClusterActionHandler> handlerMap = HandlerMapFactory.create(ImmutableSet.of(puppetHandlerFactory),
+          ImmutableSet.of(handler));
+
+    Function<ClusterSpec, ComputeServiceContext> getCompute = mock(Function.class);
+    ComputeServiceContext serviceContext = mock(ComputeServiceContext.class);
+    ComputeService computeService = mock(ComputeService.class);
+    TemplateBuilder templateBuilder = mock(TemplateBuilder.class);
+    Template template = mock(Template.class);
+
+
+    when(getCompute.apply(clusterSpec)).thenReturn(serviceContext);
+    when(serviceContext.getComputeService()).thenReturn(computeService);
+    when(computeService.templateBuilder()).thenReturn(templateBuilder);
+    when(templateBuilder.options((TemplateOptions) any())).thenReturn(templateBuilder);
+    when(templateBuilder.build()).thenReturn(template);
+    
+    Map<Set<String>, Stack<Integer>> reaction = Maps.newHashMap();
+    Stack<Integer> nnStack = new Stack<Integer>();
+    nnStack.push(new Integer(1));
+    reaction.put(nn, nnStack);
+    
+    nodeStarterFactory = new TestNodeStarterFactory(reaction);
+    BootstrapClusterAction bootstrapper = new BootstrapClusterAction(getCompute, handlerMap, nodeStarterFactory);
+    
+    bootstrapper.execute(clusterSpec, null);
+    
+    if (nodeStarterFactory != null) {
+      nodeStarterFactory.validateCompletion();
+    }
+  }  
 }

Modified: whirr/trunk/pom.xml
URL: http://svn.apache.org/viewvc/whirr/trunk/pom.xml?rev=1172926&r1=1172925&r2=1172926&view=diff
==============================================================================
--- whirr/trunk/pom.xml (original)
+++ whirr/trunk/pom.xml Tue Sep 20 00:44:22 2011
@@ -49,6 +49,7 @@
     <module>services/voldemort</module>
     <module>services/elasticsearch</module>
     <module>services/hama</module>
+    <module>services/puppet</module>
   </modules>
 
   <properties>
@@ -358,6 +359,7 @@
             <exclude>.git/**</exclude>
             <exclude>.gitignore</exclude>
             <exclude>**/*.json</exclude>
+            <exclude>**/src/test/resources/*.txt</exclude>
             <exclude>**/src/main/resources/version-banner.txt</exclude>
             <exclude>docs/**</exclude>
             <exclude>**/*.log*</exclude>

Added: whirr/trunk/recipes/puppet-http-ec2.properties
URL: http://svn.apache.org/viewvc/whirr/trunk/recipes/puppet-http-ec2.properties?rev=1172926&view=auto
==============================================================================
--- whirr/trunk/recipes/puppet-http-ec2.properties (added)
+++ whirr/trunk/recipes/puppet-http-ec2.properties Tue Sep 20 00:44:22 2011
@@ -0,0 +1,50 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+# Puppet managed http cluster
+# 
+
+# Read the Configuration Guide for more info:
+# http://whirr.apache.org/docs/latest/configuration-guide.html 
+
+# Change the cluster name here
+whirr.cluster-name=puppettest
+
+# Change the number of machines in the cluster here
+whirr.instance-templates=1 puppet:apache+puppet:ntp
+whirr.firewall-rules=80
+
+# For EC2 set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables.
+whirr.provider=aws-ec2
+whirr.identity=${env:AWS_ACCESS_KEY_ID}
+whirr.credential=${env:AWS_SECRET_ACCESS_KEY}
+
+# with puppet, you need to specify where to pull the modules from
+puppet.apache.module=git://github.com/metcalfc/puppet-apache.git
+puppet.ntp.module=git://github.com/puppetlabs/puppetlabs-ntp.git
+
+# with puppet, you can set class parameters
+ntp.servers=[ '0.pool.ntp.org', 'clock.redhat.com' ]
+ntp.autoupdate=true
+
+# By default use the user system SSH keys. Override them here.
+# whirr.private-key-file=${sys:user.home}/.ssh/id_rsa
+# whirr.public-key-file=${whirr.private-key-file}.pub
+
+# Expert: specify alternate module sources instead of from git
+#puppet.http.module=/tmp/git-puppetlabs-http.tgz

Added: whirr/trunk/recipes/puppet-http-rackspace.properties
URL: http://svn.apache.org/viewvc/whirr/trunk/recipes/puppet-http-rackspace.properties?rev=1172926&view=auto
==============================================================================
--- whirr/trunk/recipes/puppet-http-rackspace.properties (added)
+++ whirr/trunk/recipes/puppet-http-rackspace.properties Tue Sep 20 00:44:22 2011
@@ -0,0 +1,50 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+# Puppet managed http cluster
+# 
+
+# Read the Configuration Guide for more info:
+# http://whirr.apache.org/docs/latest/configuration-guide.html 
+
+# Change the cluster name here
+whirr.cluster-name=puppettest
+
+# Change the number of machines in the cluster here
+whirr.instance-templates=1 puppet:apache+puppet:ntp
+whirr.firewall-rules=80
+
+# For Rackspace set RACKSPACE_USERNAME and RACKSPACE_API_KEY environment variables.
+whirr.provider=cloudservers-us
+whirr.identity=${env:RACKSPACE_USERNAME}
+whirr.credential=${env:RACKSPACE_API_KEY}
+
+# with puppet, you need to specify where to pull the modules from
+puppet.apache.module=git://github.com/metcalfc/puppet-apache.git
+puppet.ntp.module=git://github.com/puppetlabs/puppetlabs-ntp.git
+
+# with puppet, you can set class parameters
+ntp.servers=[ '0.pool.ntp.org', 'clock.redhat.com' ]
+ntp.autoupdate=true
+
+# By default use the user system SSH keys. Override them here.
+# whirr.private-key-file=${sys:user.home}/.ssh/id_rsa
+# whirr.public-key-file=${whirr.private-key-file}.pub
+
+# Expert: specify alternate module sources instead of from git
+#puppet.http.module=/tmp/git-puppetlabs-http.tgz

Added: whirr/trunk/services/puppet/pom.xml
URL: http://svn.apache.org/viewvc/whirr/trunk/services/puppet/pom.xml?rev=1172926&view=auto
==============================================================================
--- whirr/trunk/services/puppet/pom.xml (added)
+++ whirr/trunk/services/puppet/pom.xml Tue Sep 20 00:44:22 2011
@@ -0,0 +1,78 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.whirr</groupId>
+    <artifactId>whirr</artifactId>
+    <version>0.7.0-SNAPSHOT</version>
+    <relativePath>../../pom.xml</relativePath>
+  </parent>
+  <artifactId>whirr-puppet</artifactId>
+  <packaging>jar</packaging>
+  <name>Apache Whirr Puppet</name>
+  <dependencies>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>whirr-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>whirr-core</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.hamcrest</groupId>
+      <artifactId>hamcrest-all</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-configuration</groupId>
+      <artifactId>commons-configuration</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-httpclient</groupId>
+      <artifactId>commons-httpclient</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.jcraft</groupId>
+      <artifactId>jsch</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>

Added: whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/Manifest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/Manifest.java?rev=1172926&view=auto
==============================================================================
--- whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/Manifest.java (added)
+++ whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/Manifest.java Tue Sep 20 00:44:22 2011
@@ -0,0 +1,138 @@
+/**
+ * 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.puppet;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.google.common.annotations.VisibleForTesting;
+
+/**
+ * Class representing a single Puppet Module. The module must already be available on the module
+ * path.</p>
+ * 
+ * Typical usage is: <br/>
+ * <blockquote>
+ * 
+ * <pre>
+ * ...
+ * 
+ * @Override
+ * protected void beforeBootstrap(ClusterActionEvent event) throws
+ *    IOException, InterruptedException {
+ * 
+ *  ...
+ * 
+ *  Module nginx = new Module("nginx", null);
+ *  addStatement(event, nginx);
+ *  ...
+ *  OR
+ * 
+ *  Module ntp = new Module("ntp");
+ *  ntp.attribs.put("servers", "[10.0.0.1]" );
+ *  addStatement(event,java);
+ * 
+ *  OR
+ * 
+ *  Module postgresServer = new Module("postgresql","server");
+ *  Module postgresClient = new Module("postgresql","client");
+ *  addStatement(event, postgresServer);
+ *  addStatement(event, postgresClient);
+ * 
+ * }
+ * 
+ * ...
+ * </pre>
+ * 
+ * </blockquote>
+ * 
+ */
+public class Manifest {
+
+  public final String module;
+  public final String className;
+  public final Map<String, String> attribs = new LinkedHashMap<String, String>();
+
+  @VisibleForTesting
+  String fileName = null;
+
+  /**
+   * To include the default manifest from a provided module with the specified name. Equivalent to:
+   * 
+   * puppet apply -e "class { 'module': }"
+   * 
+   * @param module
+   */
+  public Manifest(String module) {
+    this(module, null);
+  }
+
+  /**
+   * To run a particular manifest from a provided module with the specified name. Equivalent to:
+   * 
+   * puppet apply -e "class { 'module::className': )"
+   * 
+   * @param module
+   * @param className
+   */
+  public Manifest(String module, String className) {
+    this.module = module;
+    this.className = className;
+    fileName = module + (className != null ? "::" + className : "") + "-" + System.currentTimeMillis();
+    fileName = fileName.replace(":+", "-"); // replace colons with hyphens so file is easier to
+    // deal with
+  }
+
+  /**
+   * Transforms the Manifest into a puppet resource that can be interpreted by the "puppet apply"
+   * command.
+   * 
+   * @return
+   */
+  public String toString() {
+    StringBuilder resource = new StringBuilder();
+    for (String s : toStringList()) {
+      if (resource.length() > 0)
+        resource.append("\n");
+      resource.append(s);
+    }
+    return resource.toString();
+  }
+
+  List<String> toStringList() {
+    List<String> result = new ArrayList<String>();
+
+    // First part of the the resource
+    // class { 'module::className':
+    result.add("class { '" + module + (className != null ? "::" + className : "") + "':");
+
+    // If we have attribs they go in as key => value pairs
+    // These go in _unquoted_; user is responsible for supplying the quotes
+    for (Map.Entry<String, String> entry : attribs.entrySet()) {
+      result.add("  " + entry.getKey() + " => " + entry.getValue() + ",");
+    }
+
+    // Close the resource
+    result.add("}");
+
+    return result;
+  }
+}

Added: whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/PuppetClusterActionHandler.java
URL: http://svn.apache.org/viewvc/whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/PuppetClusterActionHandler.java?rev=1172926&view=auto
==============================================================================
--- whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/PuppetClusterActionHandler.java (added)
+++ whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/PuppetClusterActionHandler.java Tue Sep 20 00:44:22 2011
@@ -0,0 +1,110 @@
+/**
+ * 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.puppet;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.whirr.service.puppet.PuppetConstants.PUPPET;
+import static org.apache.whirr.service.puppet.predicates.PuppetPredicates.isFirstPuppetRoleIn;
+import static org.apache.whirr.service.puppet.predicates.PuppetPredicates.isLastPuppetRoleIn;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.whirr.service.ClusterActionEvent;
+import org.apache.whirr.service.puppet.functions.InstallAllModulesStatementFromProperties;
+import org.apache.whirr.service.puppet.functions.ModulePropertiesFromConfiguration;
+import org.apache.whirr.service.puppet.functions.RolesManagedByPuppet;
+import org.apache.whirr.service.puppet.functions.StatementToInstallModule;
+import org.apache.whirr.service.puppet.statements.CreateSitePpAndApplyRoles;
+import org.jclouds.scriptbuilder.domain.Statement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Function;
+
+/**
+ * Installs puppet. After this service is configured other services can use puppet to setup/start
+ * other services.
+ * 
+ */
+public class PuppetClusterActionHandler extends PuppetInstallClusterActionHandler {
+  static final Logger LOG = LoggerFactory.getLogger(PuppetClusterActionHandler.class);
+
+  private final String role;
+  private final Function<ClusterActionEvent, StatementToInstallModule> getStatementToInstallModuleForAction;
+
+  public PuppetClusterActionHandler(String role) {
+    this(role, new Function<ClusterActionEvent, StatementToInstallModule>() {
+
+      @Override
+      public StatementToInstallModule apply(ClusterActionEvent arg0) {
+        return new StatementToInstallModule(arg0);
+      }
+
+    });
+  }
+
+  /**
+   * @param getStatementToInstallModuleForAction allows you to override to facilitate testing
+   */
+  public PuppetClusterActionHandler(String role,
+        Function<ClusterActionEvent, StatementToInstallModule> getStatementToInstallModuleForAction) {
+    this.role = checkNotNull(role, "role");
+    this.getStatementToInstallModuleForAction = checkNotNull(getStatementToInstallModuleForAction,
+          "getStatementToInstallModuleForAction");
+  }
+
+  @Override
+  public String getRole() {
+    return role;
+  }
+
+  @Override
+  protected void beforeBootstrap(ClusterActionEvent event) throws IOException, InterruptedException {
+    if (isFirstPuppetRoleIn(event.getInstanceTemplate().getRoles()).apply(getRole())) {
+      // install puppet when bootstrapping the first puppet role
+      super.beforeBootstrap(event);
+      installAllKnownModules(event);
+    }
+
+  }
+
+  private void installAllKnownModules(ClusterActionEvent event) throws IOException {
+    Map<String, String> moduleProps = ModulePropertiesFromConfiguration.INSTANCE.apply(event.getClusterSpec()
+          .getConfigurationForKeysWithPrefix(PUPPET));
+
+    StatementToInstallModule statementMaker = getStatementToInstallModuleForAction.apply(event);
+    Statement installModules = new InstallAllModulesStatementFromProperties(statementMaker).apply(moduleProps);
+
+    event.getStatementBuilder().addStatement(installModules);
+    LOG.debug("Puppet finished installing modules for " + event.getInstanceTemplate());
+  }
+
+  protected void beforeConfigure(ClusterActionEvent event) throws IOException, InterruptedException {
+    super.beforeConfigure(event);
+
+    if (isLastPuppetRoleIn(event.getInstanceTemplate().getRoles()).apply(getRole())) {
+      Configuration config = event.getClusterSpec().getConfiguration();
+      Iterable<String> roles = RolesManagedByPuppet.INSTANCE.apply(event.getInstanceTemplate().getRoles());
+      addStatement(event, new CreateSitePpAndApplyRoles(roles, config));
+    }
+
+  }
+}

Added: whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/PuppetClusterActionHandlerFactory.java
URL: http://svn.apache.org/viewvc/whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/PuppetClusterActionHandlerFactory.java?rev=1172926&view=auto
==============================================================================
--- whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/PuppetClusterActionHandlerFactory.java (added)
+++ whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/PuppetClusterActionHandlerFactory.java Tue Sep 20 00:44:22 2011
@@ -0,0 +1,42 @@
+/**
+ * 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.puppet;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.whirr.service.puppet.PuppetConstants.PUPPET_ROLE_PREFIX;
+
+import org.apache.whirr.service.ClusterActionHandler;
+import org.apache.whirr.service.ClusterActionHandlerFactory;
+
+/**
+ * 
+ */
+public class PuppetClusterActionHandlerFactory extends ClusterActionHandlerFactory {
+
+  @Override
+  public String getRolePrefix() {
+    return PUPPET_ROLE_PREFIX;
+  }
+
+  @Override
+  public ClusterActionHandler create(String roleName) {
+    return new PuppetClusterActionHandler(checkNotNull(roleName, "roleName"));
+  }
+
+}

Added: whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/PuppetConstants.java
URL: http://svn.apache.org/viewvc/whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/PuppetConstants.java?rev=1172926&view=auto
==============================================================================
--- whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/PuppetConstants.java (added)
+++ whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/PuppetConstants.java Tue Sep 20 00:44:22 2011
@@ -0,0 +1,36 @@
+/**
+ * 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.puppet;
+
+import java.util.regex.Pattern;
+
+/**
+ * 
+ */
+public class PuppetConstants {
+  public static final String PUPPET = "puppet";
+  public static final String PUPPET_ROLE_PREFIX = PUPPET + ":";
+  public static final String MODULE_SOURCE_SUBKEY = "module";
+
+  public static final String MODULES_DIR = "/etc/puppet/modules/";
+  public static final String SITE_PP_FILE_LOCATION = "/etc/puppet/manifests/site.pp";
+
+  public static final Pattern MODULE_KEY_PATTERN = Pattern.compile("^" + PUPPET + "\\.([^.]+)\\."
+        + MODULE_SOURCE_SUBKEY + "$");
+}

Added: whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/PuppetInstallClusterActionHandler.java
URL: http://svn.apache.org/viewvc/whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/PuppetInstallClusterActionHandler.java?rev=1172926&view=auto
==============================================================================
--- whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/PuppetInstallClusterActionHandler.java (added)
+++ whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/PuppetInstallClusterActionHandler.java Tue Sep 20 00:44:22 2011
@@ -0,0 +1,54 @@
+/**
+ * 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.puppet;
+
+import static org.jclouds.scriptbuilder.domain.Statements.call;
+
+import java.io.IOException;
+
+import org.apache.whirr.service.ClusterActionEvent;
+import org.apache.whirr.service.ClusterActionHandlerSupport;
+
+/**
+ * Installs puppet (and ruby). 
+ * After this service is configured other services can use it to setup/start other services.
+ * 
+ * To test manually, run whirr launch-cluster with 1 puppet node, then ssh and confirm puppet exists
+ */
+public class PuppetInstallClusterActionHandler extends ClusterActionHandlerSupport {
+
+  public static final String PUPPET_INSTALL_ROLE = "puppet-install";
+
+  @Override
+  public String getRole() {
+   return PUPPET_INSTALL_ROLE;
+  }
+
+  @Override
+  protected void beforeBootstrap(ClusterActionEvent event) throws IOException,
+    InterruptedException {
+   
+   // install ruby and ruby-gems in the nodes
+   // (NB script is same as that in whirr-chef patch, WHIRR-49)
+   addStatement(event, call("install_ruby"));
+
+   // install puppet
+   addStatement(event, call("install_puppet"));
+  }
+}

Added: whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/InstallAllModulesStatementFromProperties.java
URL: http://svn.apache.org/viewvc/whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/InstallAllModulesStatementFromProperties.java?rev=1172926&view=auto
==============================================================================
--- whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/InstallAllModulesStatementFromProperties.java (added)
+++ whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/InstallAllModulesStatementFromProperties.java Tue Sep 20 00:44:22 2011
@@ -0,0 +1,60 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.whirr.service.puppet.functions;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Predicates.contains;
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.transform;
+import static com.google.common.collect.Maps.filterKeys;
+import static org.apache.whirr.service.puppet.PuppetConstants.MODULE_KEY_PATTERN;
+import static org.apache.whirr.service.puppet.predicates.PuppetPredicates.isModuleSubKey;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.jclouds.scriptbuilder.domain.Statement;
+import org.jclouds.scriptbuilder.domain.StatementList;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
+
+
+public class InstallAllModulesStatementFromProperties implements Function<Map<String, String>, Statement> {
+   private final StatementToInstallModule statementMaker;
+
+   public InstallAllModulesStatementFromProperties(StatementToInstallModule statementMaker) {
+      this.statementMaker = checkNotNull(statementMaker, "statementMaker");
+   }
+
+   @Override
+   public Statement apply(Map<String, String> moduleProps) {
+      Set<String> allModules = ImmutableSet.copyOf(transform(
+               filter(moduleProps.keySet(), contains(MODULE_KEY_PATTERN)), KeyToModuleNameOrNull.INSTANCE));
+
+      Builder<Statement> statements = ImmutableSet.<Statement> builder();
+      for (String module : allModules) {
+         statements.add(statementMaker.apply(filterKeys(moduleProps, isModuleSubKey(module))));
+      }
+      StatementList installModules = new StatementList(statements.build());
+      return installModules;
+   }
+
+}
+

Added: whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/KeyToModuleNameOrNull.java
URL: http://svn.apache.org/viewvc/whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/KeyToModuleNameOrNull.java?rev=1172926&view=auto
==============================================================================
--- whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/KeyToModuleNameOrNull.java (added)
+++ whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/KeyToModuleNameOrNull.java Tue Sep 20 00:44:22 2011
@@ -0,0 +1,37 @@
+/**
+ * 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.puppet.functions;
+
+import static com.google.common.base.Predicates.contains;
+import static org.apache.whirr.service.puppet.PuppetConstants.MODULE_KEY_PATTERN;
+import static org.apache.whirr.service.puppet.PuppetConstants.PUPPET;
+
+import com.google.common.base.Function;
+
+public enum KeyToModuleNameOrNull implements Function<String, String> {
+  INSTANCE;
+  @Override
+  public String apply(String arg0) {
+    if (!contains(MODULE_KEY_PATTERN).apply(arg0))
+      return null;
+    return arg0.substring(PUPPET.length() + 1).replaceAll("\\..*", "");
+  }
+
+}
+

Added: whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/ModulePropertiesFromConfiguration.java
URL: http://svn.apache.org/viewvc/whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/ModulePropertiesFromConfiguration.java?rev=1172926&view=auto
==============================================================================
--- whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/ModulePropertiesFromConfiguration.java (added)
+++ whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/ModulePropertiesFromConfiguration.java Tue Sep 20 00:44:22 2011
@@ -0,0 +1,60 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.whirr.service.puppet.functions;
+
+import static org.apache.whirr.service.puppet.PuppetConstants.MODULE_SOURCE_SUBKEY;
+import static org.apache.whirr.service.puppet.PuppetConstants.PUPPET;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.configuration.Configuration;
+
+import com.google.common.base.Function;
+import com.google.common.base.Functions;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Sets;
+import com.google.common.collect.ImmutableMap.Builder;
+
+@SuppressWarnings("unchecked")
+public enum ModulePropertiesFromConfiguration implements Function<Configuration, Map<String, String>> {
+  INSTANCE;
+  public Map<String, String> apply(final Configuration config) {
+    Iterator<String> allKeys = Iterators.transform(config.getKeys(), Functions.toStringFunction());
+    Set<String> moduleKeys = Sets.newHashSet(Iterators.<String> filter(allKeys, Predicates.and(Predicates
+          .containsPattern(PUPPET + "\\.[^.]+\\." + MODULE_SOURCE_SUBKEY), new Predicate<String>() {
+
+      @Override
+      public boolean apply(String arg0) {
+        // TODO not sure that we have to check this
+        return config.getString(arg0, null) != null;
+      }
+
+    })));
+    Builder<String, String> builder = ImmutableMap.<String, String> builder();
+    for (String key : moduleKeys) {
+      builder.put(key, config.getString(key));
+    }
+    return builder.build();
+  }
+}

Added: whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/RolesManagedByPuppet.java
URL: http://svn.apache.org/viewvc/whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/RolesManagedByPuppet.java?rev=1172926&view=auto
==============================================================================
--- whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/RolesManagedByPuppet.java (added)
+++ whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/RolesManagedByPuppet.java Tue Sep 20 00:44:22 2011
@@ -0,0 +1,40 @@
+/**
+ * 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.puppet.functions;
+
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.transform;
+import static org.apache.whirr.service.puppet.PuppetConstants.PUPPET_ROLE_PREFIX;
+import static org.apache.whirr.service.puppet.predicates.PuppetPredicates.isPuppetRole;
+
+import com.google.common.base.Function;
+
+public enum RolesManagedByPuppet implements Function<Iterable<String>, Iterable<String>> {
+  INSTANCE;
+  public Iterable<String> apply(Iterable<String> roles) {
+    return transform(filter(roles, isPuppetRole()), new Function<String, String>() {
+
+      @Override
+      public String apply(String arg0) {
+        return arg0.replaceFirst(PUPPET_ROLE_PREFIX, "");
+      }
+
+    });
+  }
+}

Added: whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/StatementToInstallModule.java
URL: http://svn.apache.org/viewvc/whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/StatementToInstallModule.java?rev=1172926&view=auto
==============================================================================
--- whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/StatementToInstallModule.java (added)
+++ whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/functions/StatementToInstallModule.java Tue Sep 20 00:44:22 2011
@@ -0,0 +1,118 @@
+/**
+ * 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.puppet.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Predicates.contains;
+import static com.google.common.collect.Iterables.find;
+import static java.lang.String.format;
+import static org.apache.whirr.service.puppet.PuppetConstants.MODULES_DIR;
+import static org.apache.whirr.service.puppet.PuppetConstants.MODULE_KEY_PATTERN;
+import static org.apache.whirr.service.puppet.PuppetConstants.PUPPET;
+import static org.jclouds.scriptbuilder.domain.Statements.call;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import org.apache.whirr.service.ClusterActionEvent;
+import org.apache.whirr.service.ClusterActionHandlerSupport;
+import org.apache.whirr.service.puppet.statements.InstallModuleFromGit;
+import org.jclouds.scriptbuilder.domain.Statement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+
+public class StatementToInstallModule implements Function<Map<String, String>, Statement> {
+  static final Logger LOG = LoggerFactory.getLogger(StatementToInstallModule.class);
+
+  private final PrepareRemoteFileUrl prepareRemoteFileUrl;
+
+  @VisibleForTesting
+  public static interface PrepareRemoteFileUrl {
+    /**
+     * Prepare the file url for the remote machine.
+     * 
+     * For public urls this function does nothing. For local urls it uploads the files to a
+     * temporary blob cache and adds download statement.
+     * 
+     * @param rawUrl
+     *        raw url as provided in the configuration file
+     * @return an URL visible to the install / configure scripts
+     * @throws IOException
+     */
+    String apply(String rawUrl) throws IOException;
+  }
+
+  public static class PrepareRemoteFileUrlUsingBlobCache implements PrepareRemoteFileUrl {
+
+    private final ClusterActionEvent event;
+
+    public PrepareRemoteFileUrlUsingBlobCache(ClusterActionEvent event) {
+      this.event = checkNotNull(event, "event");
+
+    }
+
+    /**
+     * @see ClusterActionHandlerSupport#prepareRemoteFileUrl
+     */
+    @Override
+    public String apply(String rawUrl) throws IOException {
+      return ClusterActionHandlerSupport.prepareRemoteFileUrl(event, rawUrl);
+    }
+
+  }
+
+  public StatementToInstallModule(ClusterActionEvent event) {
+    this(new PrepareRemoteFileUrlUsingBlobCache(event));
+  }
+
+  public StatementToInstallModule(PrepareRemoteFileUrl prepareRemoteFileUrl) {
+    this.prepareRemoteFileUrl = checkNotNull(prepareRemoteFileUrl, "prepareRemoteFileUrl");
+  }
+
+  @Override
+  public Statement apply(Map<String, String> props) {
+    try {
+      return installModuleFromRemoteFileOrGit(find(props.keySet(), contains(MODULE_KEY_PATTERN)), props);
+    } catch (NoSuchElementException e) {
+      throw new IllegalArgumentException(format("couldn't find pattern: %s in properties %s", MODULE_KEY_PATTERN,
+            props));
+    }
+  }
+
+  private Statement installModuleFromRemoteFileOrGit(String roleKey, Map<String, String> props) {
+    String moduleName = roleKey.substring(PUPPET.length() + 1).replaceAll("\\..*", "");
+    URI srcUri = URI.create(props.get(roleKey));
+    if ("git".equals(srcUri.getScheme())) {
+      return new InstallModuleFromGit(moduleName, srcUri, props.get(roleKey + ".branch"));
+    } else {
+      try {
+        String remotelyAccessibleUrl = prepareRemoteFileUrl.apply(srcUri.toASCIIString());
+        return call("install_tarball", remotelyAccessibleUrl, MODULES_DIR + moduleName);
+      } catch (IOException e) {
+        throw new IllegalArgumentException("problem getting src: " + srcUri, e);
+      }
+    }
+  }
+
+}

Added: whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/predicates/PuppetPredicates.java
URL: http://svn.apache.org/viewvc/whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/predicates/PuppetPredicates.java?rev=1172926&view=auto
==============================================================================
--- whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/predicates/PuppetPredicates.java (added)
+++ whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/predicates/PuppetPredicates.java Tue Sep 20 00:44:22 2011
@@ -0,0 +1,91 @@
+/**
+ * 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.puppet.predicates;
+
+import static org.apache.whirr.service.puppet.PuppetConstants.MODULE_SOURCE_SUBKEY;
+import static org.apache.whirr.service.puppet.PuppetConstants.PUPPET;
+import static org.apache.whirr.service.puppet.PuppetConstants.PUPPET_ROLE_PREFIX;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+
+/**
+ * 
+ */
+public class PuppetPredicates {
+  public static Predicate<String> isFirstPuppetRoleIn(final Iterable<String> roles) {
+    return new Predicate<String>() {
+
+      @Override
+      public boolean apply(String arg0) {
+        return Iterables.get(Iterables.filter(roles, Predicates.containsPattern("^" + PUPPET_ROLE_PREFIX + arg0)),
+              0).equals(PUPPET_ROLE_PREFIX + arg0);
+      }
+
+      @Override
+      public String toString() {
+        return "isFirstPuppetRoleIn(" + roles + ")";
+
+      }
+    };
+
+  }
+
+  public static Predicate<CharSequence> isPuppetRole() {
+    return Predicates.containsPattern("^" + PUPPET_ROLE_PREFIX);
+  }
+
+  public static Predicate<String> isLastPuppetRoleIn(final Iterable<String> roles) {
+    return new Predicate<String>() {
+
+      @Override
+      public boolean apply(String arg0) {
+        return Iterables.getLast(
+              Iterables.filter(roles, Predicates.containsPattern("^" + PUPPET_ROLE_PREFIX + arg0))).equals(
+              PUPPET_ROLE_PREFIX + arg0);
+      }
+
+      @Override
+      public String toString() {
+        return "isLastPuppetRoleIn(" + roles + ")";
+
+      }
+    };
+
+  }
+  
+
+  public static Predicate<String> isModuleSubKey(final String module) {
+    return new Predicate<String>() {
+
+      @Override
+      public boolean apply(String arg0) {
+        return arg0.startsWith(PUPPET + "." + module + "." + MODULE_SOURCE_SUBKEY);
+      }
+
+      @Override
+      public String toString() {
+        return "isModuleSubKey(" + module + ")";
+
+      }
+    };
+  }
+}
+

Added: whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/statements/CreateSitePpAndApplyRoles.java
URL: http://svn.apache.org/viewvc/whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/statements/CreateSitePpAndApplyRoles.java?rev=1172926&view=auto
==============================================================================
--- whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/statements/CreateSitePpAndApplyRoles.java (added)
+++ whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/statements/CreateSitePpAndApplyRoles.java Tue Sep 20 00:44:22 2011
@@ -0,0 +1,141 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * 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.puppet.statements;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.whirr.service.puppet.PuppetConstants.SITE_PP_FILE_LOCATION;
+import static org.jclouds.scriptbuilder.domain.Statements.appendFile;
+import static org.jclouds.scriptbuilder.domain.Statements.exec;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.apache.whirr.service.puppet.Manifest;
+import org.jclouds.scriptbuilder.domain.OsFamily;
+import org.jclouds.scriptbuilder.domain.Statement;
+import org.jclouds.scriptbuilder.domain.StatementList;
+import org.jclouds.scriptbuilder.domain.Statements;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableList.Builder;
+
+public class CreateSitePpAndApplyRoles implements Statement {
+  private Iterable<String> roles;
+  private Configuration config;
+
+  public CreateSitePpAndApplyRoles(Iterable<String> roles, Configuration config) {
+    this.roles = checkNotNull(roles, "roles");
+    this.config = checkNotNull(config, "config");
+  }
+
+  @Override
+  public Iterable<String> functionDependencies(OsFamily arg0) {
+    return ImmutableSet.of();
+  }
+
+  @Override
+  public String render(OsFamily arg0) {
+
+    // when we get to the last role, let's cat all the manifests we made together inside a
+    // node default site.pp
+    Builder<Statement> statements = ImmutableList.<Statement> builder();
+
+    statements.add(Statements.rm(SITE_PP_FILE_LOCATION));
+    Builder<String> sitePp = ImmutableList.<String> builder();
+
+    sitePp.add("node default {");
+    for (String role : roles) {
+      String manifestAttribPrefix = role.replaceAll(":+", ".");
+      Configuration manifestProps = new PropertiesConfiguration();
+      for (@SuppressWarnings("unchecked")
+      Iterator<String> it = config.getKeys(manifestAttribPrefix); it.hasNext();) {
+        String key = it.next();
+        manifestProps.setProperty(key, config.getProperty(key));
+      }
+      sitePp.add(getManifestForClusterSpecAndRole(role, manifestProps).toString());
+    }
+    sitePp.add("}");
+
+    statements.add(appendFile(SITE_PP_FILE_LOCATION, sitePp.build()));
+    statements.add(exec("puppet apply " + SITE_PP_FILE_LOCATION));
+
+    return new StatementList(statements.build()).render(arg0);
+  }
+
+  static final Logger LOG = LoggerFactory.getLogger(CreateSitePpAndApplyRoles.class);
+
+  // TODO refactor this
+  @SuppressWarnings("unchecked")
+  @VisibleForTesting
+  static Manifest getManifestForClusterSpecAndRole(String subroleModuleManifest, Configuration manifestProps) {
+    int firstColon = subroleModuleManifest.indexOf(':');
+    String moduleName, manifestClassName;
+    if (firstColon == -1) {
+      moduleName = subroleModuleManifest;
+      manifestClassName = null;
+    } else {
+      moduleName = subroleModuleManifest.substring(0, firstColon);
+      int firstDoubleColon = subroleModuleManifest.indexOf("::");
+      if (firstDoubleColon != firstColon)
+        throw new IllegalArgumentException("Malformed subrole spec for role puppet: "
+              + "format should be puppet:module or puppet:module::manifest");
+      manifestClassName = subroleModuleManifest.substring(firstDoubleColon + 2);
+    }
+
+    // now create and populate the manifest
+    Manifest manifest = new Manifest(moduleName, manifestClassName);
+
+    for (Iterator<?> longkeyI = manifestProps.getKeys(); longkeyI.hasNext();) {
+      String longkey = (String) longkeyI.next();
+      String key = longkey.substring(subroleModuleManifest.replaceAll(":+", ".").length() + 1);
+      if (key.indexOf('.') >= 0) {
+        // it's for a sub-manifest; skip it
+      } else {
+        Object value = manifestProps.getProperty(longkey);
+        // an array, e.g. ['1', '2'] gets parsed as a list of two strings, which we need to join
+        // with ", "
+        String vs = "";
+        if (value == null)
+          LOG.warn("Invalid value for key " + longkey + ": null"); // shouldn't happen
+        else if (value instanceof Collection) {
+          Iterator<?> vi = ((Collection<?>) value).iterator();
+          if (!vi.hasNext())
+            LOG.warn("Invalid value for key " + longkey + ": empty list"); // shouldn't happen
+          else {
+            vs += vi.next();
+            while (vi.hasNext())
+              vs += ", " + vi.next();
+          }
+        } else {
+          vs = value.toString();
+        }
+        manifest.attribs.put(key, vs);
+      }
+    }
+    LOG.debug("Bootstrapping " + subroleModuleManifest + ", produced manifest:\n" + manifest);
+    return manifest;
+  }
+}

Added: whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/statements/InstallModuleFromGit.java
URL: http://svn.apache.org/viewvc/whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/statements/InstallModuleFromGit.java?rev=1172926&view=auto
==============================================================================
--- whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/statements/InstallModuleFromGit.java (added)
+++ whirr/trunk/services/puppet/src/main/java/org/apache/whirr/service/puppet/statements/InstallModuleFromGit.java Tue Sep 20 00:44:22 2011
@@ -0,0 +1,105 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * 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.puppet.statements;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.whirr.service.puppet.PuppetConstants.MODULES_DIR;
+import static org.jclouds.scriptbuilder.domain.Statements.exec;
+
+import java.net.URI;
+
+import javax.annotation.Nullable;
+
+import org.jclouds.scriptbuilder.domain.OsFamily;
+import org.jclouds.scriptbuilder.domain.Statement;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Class representing a single Puppet Module. Clones a Puppet module to /etc/puppet/modules.</p>
+ * 
+ * Typical usage is: <br/>
+ * <blockquote>
+ * 
+ * <pre>
+ * ...
+ * 
+ * @Override
+ * protected void beforeBootstrap(ClusterActionEvent event) throws
+ *    IOException, InterruptedException {
+ * 
+ *  ...
+ * 
+ *  nginx = new InstallModuleFromGit("nginx", "git://github.com/puppetlabs/puppetlabs-nginx.git")
+ *  addStatement(event, nginx);
+ * 
+ *  OR
+ * 
+ *  fw = new InstallModuleFromGit("fw", "git://github.com/puppetlabs/puppetlabs-firewall.git", "next")
+ *  addStatement(event, fw);
+ * 
+ *  ...
+ * 
+ * }
+ * 
+ * ...
+ * </pre>
+ * 
+ * </blockquote>
+ * 
+ */
+public class InstallModuleFromGit implements Statement {
+
+  private final String module;
+  private final URI url;
+  private final String vcsBranch;
+
+  /**
+   * To clone the master branch of module from a repository located at <code>url</code>.
+   * 
+   * @param module
+   * @param url
+   */
+
+  public InstallModuleFromGit(String module, URI url) {
+    this(module, url, null);
+  }
+
+  public InstallModuleFromGit(String module, URI url, @Nullable String vcsBranch) {
+    this.module = checkNotNull(module, "module");
+    this.url = checkNotNull(url, "url");
+    checkArgument(url.getScheme().equals("git"), "not a git url: %s", url);
+    this.vcsBranch = vcsBranch;
+  }
+
+  @Override
+  public Iterable<String> functionDependencies(OsFamily arg0) {
+    return ImmutableSet.<String> of();
+  }
+
+  @Override
+  public String render(OsFamily arg0) {
+    return exec(
+          "git clone " + (vcsBranch != null ? "-b " + vcsBranch + " " : "") + url + " " + MODULES_DIR
+                + module).render(arg0);
+  }
+}
+

Added: whirr/trunk/services/puppet/src/main/resources/META-INF/services/org.apache.whirr.service.ClusterActionHandler
URL: http://svn.apache.org/viewvc/whirr/trunk/services/puppet/src/main/resources/META-INF/services/org.apache.whirr.service.ClusterActionHandler?rev=1172926&view=auto
==============================================================================
--- whirr/trunk/services/puppet/src/main/resources/META-INF/services/org.apache.whirr.service.ClusterActionHandler (added)
+++ whirr/trunk/services/puppet/src/main/resources/META-INF/services/org.apache.whirr.service.ClusterActionHandler Tue Sep 20 00:44:22 2011
@@ -0,0 +1,12 @@
+#   Licensed 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.
+org.apache.whirr.service.puppet.PuppetInstallClusterActionHandler

Added: whirr/trunk/services/puppet/src/main/resources/META-INF/services/org.apache.whirr.service.ClusterActionHandlerFactory
URL: http://svn.apache.org/viewvc/whirr/trunk/services/puppet/src/main/resources/META-INF/services/org.apache.whirr.service.ClusterActionHandlerFactory?rev=1172926&view=auto
==============================================================================
--- whirr/trunk/services/puppet/src/main/resources/META-INF/services/org.apache.whirr.service.ClusterActionHandlerFactory (added)
+++ whirr/trunk/services/puppet/src/main/resources/META-INF/services/org.apache.whirr.service.ClusterActionHandlerFactory Tue Sep 20 00:44:22 2011
@@ -0,0 +1,12 @@
+#   Licensed 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.
+org.apache.whirr.service.puppet.PuppetClusterActionHandlerFactory



Mime
View raw message