Updated to 1.7.0 and to use the Omnibus installer in Solo
diff --git a/chef-basics/pom.xml b/chef-basics/pom.xml
index 3f016ff..7773883 100644
--- a/chef-basics/pom.xml
+++ b/chef-basics/pom.xml
@@ -22,49 +22,39 @@
 <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/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
-  <groupId>org.jclouds.examples</groupId>
+  <groupId>org.apache.jclouds.examples</groupId>
   <artifactId>chef-basics</artifactId>
-  <version>1.6.0-SNAPSHOT</version>
+  <version>1.7.0</version>
   <name>chef-basics</name>
   <description>jclouds chef example that adds a node to a group, then installs an Apache web server on all nodes</description>
   
   <properties>
-    <jclouds.version>1.6.0</jclouds.version>    
+    <jclouds.version>1.7.0</jclouds.version>
   </properties>
 
-  <repositories>
-    <repository>
-      <id>jclouds-snapshots</id>
-      <url>https://oss.sonatype.org/content/repositories/snapshots</url>
-      <snapshots>
-        <enabled>true</enabled>
-      </snapshots>
-    </repository>
-  </repositories>
-
   <dependencies>
     <dependency>
-      <groupId>org.jclouds</groupId>
+      <groupId>org.apache.jclouds</groupId>
       <artifactId>jclouds-compute</artifactId>
       <version>${jclouds.version}</version>
     </dependency>
     <dependency>
-      <groupId>org.jclouds.api</groupId>
+      <groupId>org.apache.jclouds.api</groupId>
       <artifactId>chef</artifactId>
       <version>${jclouds.version}</version>
     </dependency>
     <dependency>
-      <groupId>org.jclouds</groupId>
+      <groupId>org.apache.jclouds</groupId>
       <artifactId>jclouds-allcompute</artifactId>
       <version>${jclouds.version}</version>
     </dependency>
     <dependency>
-      <groupId>org.jclouds.labs</groupId>
+      <groupId>org.apache.jclouds.labs</groupId>
       <artifactId>joyentcloud</artifactId>
       <version>${jclouds.version}</version>
     </dependency>
     <dependency>
-      <groupId>org.jclouds.labs</groupId>
+      <groupId>org.apache.jclouds.labs</groupId>
       <artifactId>virtualbox</artifactId>
       <version>${jclouds.version}</version>
     </dependency>      
@@ -73,7 +63,7 @@
          like below -->
     <!--
         <dependency>
-        <groupId>org.jclouds.provider</groupId>
+        <groupId>org.apache.jclouds.provider</groupId>
         <artifactId>gogrid</artifactId>
         <version>${jclouds.version}</version>
         </dependency>
@@ -82,7 +72,7 @@
          the abiquo maven repository to get its dependencies.
          See jclouds/labs/abiquo/pom.xml
          <dependency>
-         <groupId>org.jclouds.labs</groupId>
+         <groupId>org.apache.jclouds.labs</groupId>
          <artifactId>abiquo</artifactId>
          <version>${jclouds.version}</version>
          </dependency>
@@ -94,7 +84,7 @@
       <scope>provided</scope>
     </dependency>
     <dependency>
-      <groupId>org.jclouds.driver</groupId>
+      <groupId>org.apache.jclouds.driver</groupId>
       <artifactId>jclouds-bouncycastle</artifactId>
       <version>${jclouds.version}</version>
       <exclusions>
@@ -110,12 +100,12 @@
       </exclusions>
     </dependency>
     <dependency>
-      <groupId>org.jclouds.driver</groupId>
+      <groupId>org.apache.jclouds.driver</groupId>
       <artifactId>jclouds-sshj</artifactId>
       <version>${jclouds.version}</version>
     </dependency>
     <dependency>
-      <groupId>org.jclouds.driver</groupId>
+      <groupId>org.apache.jclouds.driver</groupId>
       <artifactId>jclouds-enterprise</artifactId>
       <version>${jclouds.version}</version>
     </dependency>
@@ -142,7 +132,7 @@
         <configuration>
           <archive>
             <manifest>
-              <mainClass>org.jclouds.examples.chef.basics.MainApp</mainClass>
+              <mainClass>org.apache.jclouds.examples.chef.basics.MainApp</mainClass>
             </manifest>
           </archive>
         </configuration>
@@ -156,7 +146,7 @@
           </descriptors>
           <archive>
             <manifest>
-              <mainClass>org.jclouds.examples.chef.basics.MainApp</mainClass>
+              <mainClass>org.apache.jclouds.examples.chef.basics.MainApp</mainClass>
             </manifest>
             <manifestEntries>
               <Class-Path>bcprov-jdk16.jar</Class-Path>
diff --git a/chef-basics/src/main/java/org/apache/jclouds/examples/chef/basics/MainApp.java b/chef-basics/src/main/java/org/apache/jclouds/examples/chef/basics/MainApp.java
new file mode 100644
index 0000000..18c2922
--- /dev/null
+++ b/chef-basics/src/main/java/org/apache/jclouds/examples/chef/basics/MainApp.java
@@ -0,0 +1,359 @@
+/*
+ * 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.jclouds.examples.chef.basics;
+
+import static com.google.common.base.Charsets.UTF_8;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Predicates.not;
+import static com.google.common.collect.Iterables.concat;
+import static com.google.common.collect.Iterables.contains;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_SCRIPT_COMPLETE;
+import static org.jclouds.compute.options.TemplateOptions.Builder.overrideLoginCredentials;
+import static org.jclouds.compute.options.TemplateOptions.Builder.runScript;
+import static org.jclouds.compute.predicates.NodePredicates.TERMINATED;
+import static org.jclouds.compute.predicates.NodePredicates.inGroup;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.jclouds.ContextBuilder;
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.Apis;
+import org.jclouds.chef.ChefApiMetadata;
+import org.jclouds.chef.ChefContext;
+import org.jclouds.chef.ChefService;
+import org.jclouds.chef.config.ChefProperties;
+import org.jclouds.chef.domain.BootstrapConfig;
+import org.jclouds.chef.util.RunListBuilder;
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.ComputeServiceContext;
+import org.jclouds.compute.RunNodesException;
+import org.jclouds.compute.RunScriptOnNodesException;
+import org.jclouds.compute.domain.ExecResponse;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.compute.domain.TemplateBuilder;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.enterprise.config.EnterpriseConfigurationModule;
+import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
+import org.jclouds.providers.ProviderMetadata;
+import org.jclouds.providers.Providers;
+import org.jclouds.scriptbuilder.domain.Statement;
+import org.jclouds.scriptbuilder.domain.StatementList;
+import org.jclouds.scriptbuilder.domain.chef.RunList;
+import org.jclouds.scriptbuilder.statements.chef.ChefSolo;
+import org.jclouds.scriptbuilder.statements.chef.InstallChefUsingOmnibus;
+import org.jclouds.scriptbuilder.statements.git.CloneGitRepo;
+import org.jclouds.scriptbuilder.statements.git.InstallGit;
+import org.jclouds.scriptbuilder.statements.login.AdminAccess;
+import org.jclouds.sshj.config.SshjSshClientModule;
+
+import com.google.common.base.Predicates;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import com.google.common.io.Files;
+import com.google.inject.Module;
+
+/**
+ * Demonstrates the use of {@link ComputeService}.
+ * <p/>
+ * Usage is: {@code java MainApp provider identity credential groupName (add|chef|destroy)} if
+ * {@code chef} is used, the following parameter is a list of recipes to be installed in the node
+ * separated by commas.
+ * 
+ * @author Adrian Cole
+ * @author Ignasi Barrera
+ */
+public class MainApp
+{
+
+    public static enum Action
+    {
+        ADD, CHEF, SOLO, DESTROY;
+    }
+
+    public static final Map<String, ApiMetadata> allApis = Maps.uniqueIndex(
+        Apis.viewableAs(ComputeServiceContext.class), Apis.idFunction());
+
+    public static final Map<String, ProviderMetadata> appProviders = Maps.uniqueIndex(
+        Providers.viewableAs(ComputeServiceContext.class), Providers.idFunction());
+
+    public static final Set<String> allKeys = ImmutableSet.copyOf(Iterables.concat(
+        appProviders.keySet(), allApis.keySet()));
+
+    public static int PARAMETERS = 5;
+
+    public static String INVALID_SYNTAX =
+        "Invalid number of parameters. Syntax is: provider identity credential groupName (add|chef|solo|destroy)";
+
+    public static void main(final String[] args)
+    {
+        if (args.length < PARAMETERS)
+        {
+            throw new IllegalArgumentException(INVALID_SYNTAX);
+        }
+
+        String provider = args[0];
+        String identity = args[1];
+        String credential = args[2];
+        String groupName = args[3];
+        Action action = Action.valueOf(args[4].toUpperCase());
+        if ((action == Action.CHEF || action == Action.SOLO) && args.length < PARAMETERS + 1)
+        {
+            throw new IllegalArgumentException("please provide the list of recipes to install, separated by commas");
+        }
+        String recipes = action == Action.CHEF || action == Action.SOLO ? args[5] : "apache2";
+
+        String minRam = System.getProperty("minRam");
+
+        // note that you can check if a provider is present ahead of time
+        checkArgument(contains(allKeys, provider), "provider %s not in supported list: %s",
+            provider, allKeys);
+
+        LoginCredentials login =
+            action != Action.DESTROY ? getLoginForCommandExecution(action) : null;
+
+        ComputeService compute = initComputeService(provider, identity, credential);
+
+        try
+        {
+            switch (action)
+            {
+                case ADD:
+                    System.out.printf(">> adding node to group %s%n", groupName);
+
+                    // Default template chooses the smallest size on an operating
+                    // system that tested to work with java
+                    TemplateBuilder templateBuilder = compute.templateBuilder();
+                    templateBuilder.osFamily(OsFamily.UBUNTU);
+
+                    // If you want to up the ram and leave everything default, you
+                    // can just tweak minRam
+                    if (minRam != null)
+                    {
+                        templateBuilder.minRam(Integer.parseInt(minRam));
+                    }
+
+                    // note this will create a user with the same name as you on the
+                    // node. ex. you can connect via ssh publicip
+                    Statement bootInstructions = AdminAccess.standard();
+
+                    // to run commands as root, we use the runScript option in the
+                    // template.
+                    templateBuilder.options(runScript(bootInstructions));
+
+                    NodeMetadata node =
+                        getOnlyElement(compute.createNodesInGroup(groupName, 1,
+                            templateBuilder.build()));
+                    System.out.printf("<< node %s: %s%n", node.getId(),
+                        concat(node.getPrivateAddresses(), node.getPublicAddresses()));
+
+                case SOLO:
+                    System.out.printf(">> installing [%s] on group %s as %s%n", recipes, groupName,
+                        login.identity);
+
+                    Iterable<String> recipeList = Splitter.on(',').split(recipes);
+                    ImmutableList.Builder<Statement> bootstrapBuilder = ImmutableList.builder();
+                    bootstrapBuilder.add(new InstallGit());
+
+                    // Clone community cookbooks into the node
+                    for (String recipe : recipeList)
+                    {
+                        bootstrapBuilder.add(CloneGitRepo.builder()
+                            .repository("git://github.com/opscode-cookbooks/" + recipe + ".git")
+                            .directory("/var/chef/cookbooks/" + recipe) //
+                            .build());
+                    }
+
+                    // Configure Chef Solo to bootstrap the selected recipes
+                    bootstrapBuilder.add(new InstallChefUsingOmnibus());
+                    bootstrapBuilder.add(ChefSolo.builder() //
+                        .cookbookPath("/var/chef/cookbooks") //
+                        .runlist(RunList.builder().recipes(recipeList).build()) //
+                        .build());
+
+                    // Build the statement that will perform all the operations above
+                    StatementList bootstrap = new StatementList(bootstrapBuilder.build());
+
+                    // Run the script in the nodes of the group
+                    runScriptOnGroup(compute, login, groupName, bootstrap);
+                    break;
+                case CHEF:
+                    // Create the connection to the Chef server
+                    ChefService chef =
+                        initChefService(System.getProperty("chef.client"),
+                            System.getProperty("chef.validator"));
+
+                    // Build the runlist for the deployed nodes
+                    System.out.println("Configuring node runlist in the Chef server...");
+                    List<String> runlist =
+                        new RunListBuilder().addRecipes(recipes.split(",")).build();
+                    BootstrapConfig config = BootstrapConfig.builder().runList(runlist).build();
+                    chef.updateBootstrapConfigForGroup(groupName, config);
+                    Statement chefServerBootstrap = chef.createBootstrapScriptForGroup(groupName);
+
+                    // Run the script in the nodes of the group
+                    System.out.printf(">> installing [%s] on group %s as %s%n", recipes, groupName,
+                        login.identity);
+                    runScriptOnGroup(compute, login, groupName, chefServerBootstrap);
+                    break;
+                case DESTROY:
+                    System.out.printf(">> destroying nodes in group %s%n", groupName);
+                    // you can use predicates to select which nodes you wish to
+                    // destroy.
+                    Set< ? extends NodeMetadata> destroyed = compute.destroyNodesMatching(//
+                        Predicates.<NodeMetadata> and(not(TERMINATED), inGroup(groupName)));
+                    System.out.printf("<< destroyed nodes %s%n", destroyed);
+                    break;
+            }
+        }
+        catch (RunNodesException e)
+        {
+            System.err.println("error adding node to group " + groupName + ": " + e.getMessage());
+            error = 1;
+        }
+        catch (RunScriptOnNodesException e)
+        {
+            System.err.println("error installing " + recipes + " on group " + groupName + ": "
+                + e.getMessage());
+            error = 1;
+        }
+        catch (Exception e)
+        {
+            System.err.println("error: " + e.getMessage());
+            error = 1;
+        }
+        finally
+        {
+            compute.getContext().close();
+            System.exit(error);
+        }
+    }
+
+    static int error = 0;
+
+    private static void runScriptOnGroup(final ComputeService compute,
+        final LoginCredentials login, final String groupName, final Statement command)
+        throws RunScriptOnNodesException
+    {
+        // when you run commands, you can pass options to decide whether
+        // to run it as root, supply or own credentials vs from cache,
+        // and wrap in an init script vs directly invoke
+        Map< ? extends NodeMetadata, ExecResponse> execResponses =
+            compute.runScriptOnNodesMatching(//
+                inGroup(groupName), // predicate used to select nodes
+                command, // what you actually intend to run
+                overrideLoginCredentials(login)); // use the local user & ssh key
+
+        for (Entry< ? extends NodeMetadata, ExecResponse> response : execResponses.entrySet())
+        {
+            System.out.printf(
+                "<< node %s: %s%n",
+                response.getKey().getId(),
+                concat(response.getKey().getPrivateAddresses(), response.getKey()
+                    .getPublicAddresses()));
+            System.out.printf("<<     %s%n", response.getValue());
+        }
+    }
+
+    private static ComputeService initComputeService(final String provider, final String identity,
+        final String credential)
+    {
+        // example of specific properties, in this case optimizing image list to
+        // only amazon supplied
+        Properties properties = new Properties();
+        long scriptTimeout = TimeUnit.MILLISECONDS.convert(20, TimeUnit.MINUTES);
+        properties.setProperty(TIMEOUT_SCRIPT_COMPLETE, scriptTimeout + "");
+
+        // example of injecting a ssh implementation
+        Iterable<Module> modules =
+            ImmutableSet.<Module> of(new SshjSshClientModule(), new SLF4JLoggingModule(),
+                new EnterpriseConfigurationModule());
+
+        ContextBuilder builder =
+            ContextBuilder.newBuilder(provider).credentials(identity, credential).modules(modules)
+                .overrides(properties);
+
+        System.out.printf(">> initializing %s%n", builder.getApiMetadata());
+
+        return builder.buildView(ComputeServiceContext.class).getComputeService();
+    }
+
+    private static ChefService initChefService(final String client, final String validator)
+    {
+        try
+        {
+            Properties chefConfig = new Properties();
+            chefConfig.put(ChefProperties.CHEF_VALIDATOR_NAME, validator);
+            chefConfig
+                .put(ChefProperties.CHEF_VALIDATOR_CREDENTIAL, credentialForClient(validator));
+
+            ContextBuilder builder = ContextBuilder.newBuilder(new ChefApiMetadata()) //
+                .credentials(client, credentialForClient(client)) //
+                .modules(ImmutableSet.<Module> of(new SLF4JLoggingModule())) //
+                .overrides(chefConfig); //
+
+            System.out.printf(">> initializing %s%n", builder.getApiMetadata());
+
+            ChefContext context = builder.build();
+            return context.getChefService();
+        }
+        catch (Exception e)
+        {
+            System.err.println("error reading private key " + e.getMessage());
+            System.exit(1);
+            return null;
+        }
+    }
+
+    private static LoginCredentials getLoginForCommandExecution(final Action action)
+    {
+        try
+        {
+            String user = System.getProperty("user.name");
+            String privateKey =
+                Files.toString(new File(System.getProperty("user.home") + "/.ssh/id_rsa"), UTF_8);
+            return LoginCredentials.builder().user(user).privateKey(privateKey)
+                .authenticateSudo(true).build();
+        }
+        catch (Exception e)
+        {
+            System.err.println("error reading ssh key " + e.getMessage());
+            System.exit(1);
+            return null;
+        }
+    }
+
+    private static String credentialForClient(final String client) throws Exception
+    {
+        String pemFile = System.getProperty("user.home") + "/.chef/" + client + ".pem";
+        return Files.toString(new File(pemFile), UTF_8);
+    }
+
+}
diff --git a/chef-basics/src/main/java/org/jclouds/examples/chef/basics/MainApp.java b/chef-basics/src/main/java/org/jclouds/examples/chef/basics/MainApp.java
deleted file mode 100644
index b120478..0000000
--- a/chef-basics/src/main/java/org/jclouds/examples/chef/basics/MainApp.java
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * 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.jclouds.examples.chef.basics;
-
-import static com.google.common.base.Charsets.UTF_8;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Predicates.not;
-import static com.google.common.collect.Iterables.concat;
-import static com.google.common.collect.Iterables.contains;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static org.jclouds.aws.ec2.reference.AWSEC2Constants.PROPERTY_EC2_AMI_QUERY;
-import static org.jclouds.aws.ec2.reference.AWSEC2Constants.PROPERTY_EC2_CC_AMI_QUERY;
-import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_SCRIPT_COMPLETE;
-import static org.jclouds.compute.options.TemplateOptions.Builder.overrideLoginCredentials;
-import static org.jclouds.compute.options.TemplateOptions.Builder.runScript;
-import static org.jclouds.compute.predicates.NodePredicates.TERMINATED;
-import static org.jclouds.compute.predicates.NodePredicates.inGroup;
-
-import java.io.File;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Properties;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-import org.jclouds.ContextBuilder;
-import org.jclouds.apis.ApiMetadata;
-import org.jclouds.apis.Apis;
-import org.jclouds.chef.ChefApiMetadata;
-import org.jclouds.chef.ChefContext;
-import org.jclouds.chef.ChefService;
-import org.jclouds.chef.config.ChefProperties;
-import org.jclouds.chef.util.RunListBuilder;
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.ComputeServiceContext;
-import org.jclouds.compute.RunNodesException;
-import org.jclouds.compute.RunScriptOnNodesException;
-import org.jclouds.compute.domain.ExecResponse;
-import org.jclouds.compute.domain.NodeMetadata;
-import org.jclouds.compute.domain.TemplateBuilder;
-import org.jclouds.domain.LoginCredentials;
-import org.jclouds.enterprise.config.EnterpriseConfigurationModule;
-import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
-import org.jclouds.providers.ProviderMetadata;
-import org.jclouds.providers.Providers;
-import org.jclouds.scriptbuilder.domain.Statement;
-import org.jclouds.scriptbuilder.domain.StatementList;
-import org.jclouds.scriptbuilder.domain.chef.RunList;
-import org.jclouds.scriptbuilder.statements.chef.ChefSolo;
-import org.jclouds.scriptbuilder.statements.git.CloneGitRepo;
-import org.jclouds.scriptbuilder.statements.git.InstallGit;
-import org.jclouds.scriptbuilder.statements.login.AdminAccess;
-import org.jclouds.scriptbuilder.statements.ruby.InstallRuby;
-import org.jclouds.scriptbuilder.statements.ruby.InstallRubyGems;
-import org.jclouds.sshj.config.SshjSshClientModule;
-
-import com.google.common.base.Predicates;
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Maps;
-import com.google.common.io.Files;
-import com.google.inject.Module;
-
-/**
- * Demonstrates the use of {@link ComputeService}.
- * <p/>
- * Usage is:
- * {@code java MainApp provider identity credential groupName (add|chef|destroy)}
- * if {@code chef} is used, the following parameter is a list of recipes to be
- * installed in the node separated by commas.
- * 
- * @author Adrian Cole
- * @author Ignasi Barrera
- */
-public class MainApp {
-
-   public static enum Action {
-      ADD, CHEF, SOLO, DESTROY;
-   }
-
-   public static final Map<String, ApiMetadata> allApis = Maps.uniqueIndex(
-         Apis.viewableAs(ComputeServiceContext.class), Apis.idFunction());
-
-   public static final Map<String, ProviderMetadata> appProviders = Maps.uniqueIndex(
-         Providers.viewableAs(ComputeServiceContext.class), Providers.idFunction());
-
-   public static final Set<String> allKeys = ImmutableSet.copyOf(Iterables.concat(appProviders.keySet(),
-         allApis.keySet()));
-
-   public static int PARAMETERS = 5;
-   public static String INVALID_SYNTAX = "Invalid number of parameters. Syntax is: provider identity credential groupName (add|chef|solo|destroy)";
-
-   public static void main(String[] args) {
-      if (args.length < PARAMETERS) {
-         throw new IllegalArgumentException(INVALID_SYNTAX);
-      }
-
-      String provider = args[0];
-      String identity = args[1];
-      String credential = args[2];
-      String groupName = args[3];
-      Action action = Action.valueOf(args[4].toUpperCase());
-      if ((action == Action.CHEF || action == Action.SOLO) && args.length < PARAMETERS + 1) {
-         throw new IllegalArgumentException("please provide the list of recipes to install, separated by commas");
-      }
-      String recipes = action == Action.CHEF || action == Action.SOLO ? args[5] : "apache2";
-
-      String minRam = System.getProperty("minRam");
-
-      // note that you can check if a provider is present ahead of time
-      checkArgument(contains(allKeys, provider), "provider %s not in supported list: %s", provider, allKeys);
-
-      LoginCredentials login = action != Action.DESTROY ? getLoginForCommandExecution(action) : null;
-
-      ComputeService compute = initComputeService(provider, identity, credential);
-
-      try {
-         switch (action) {
-            case ADD:
-               System.out.printf(">> adding node to group %s%n", groupName);
-
-               // Default template chooses the smallest size on an operating
-               // system that tested to work with java, which tends to be Ubuntu
-               // or CentOS
-               TemplateBuilder templateBuilder = compute.templateBuilder();
-
-               // If you want to up the ram and leave everything default, you
-               // can just tweak minRam
-               if (minRam != null) {
-                  templateBuilder.minRam(Integer.parseInt(minRam));
-               }
-
-               // note this will create a user with the same name as you on the
-               // node. ex. you can connect via ssh publicip
-               Statement bootInstructions = AdminAccess.standard();
-
-               // to run commands as root, we use the runScript option in the
-               // template.
-               templateBuilder.options(runScript(bootInstructions));
-
-               NodeMetadata node = getOnlyElement(compute.createNodesInGroup(groupName, 1, templateBuilder.build()));
-               System.out.printf("<< node %s: %s%n", node.getId(),
-                     concat(node.getPrivateAddresses(), node.getPublicAddresses()));
-
-            case SOLO:
-               System.out.printf(">> installing [%s] on group %s as %s%n", recipes, groupName, login.identity);
-
-               Iterable<String> recipeList = Splitter.on(',').split(recipes);
-               ImmutableList.Builder<Statement> bootstrapBuilder = ImmutableList.builder();
-               bootstrapBuilder.add(new InstallGit());
-
-               // Clone community cookbooks into the node
-               for (String recipe : recipeList) {
-                  bootstrapBuilder.add(CloneGitRepo.builder()
-                        .repository("git://github.com/opscode-cookbooks/" + recipe + ".git")
-                        .directory("/var/chef/cookbooks/" + recipe) //
-                        .build());
-               }
-
-               // Configure Chef Solo to bootstrap the selected recipes
-               bootstrapBuilder.add(InstallRuby.builder().build());
-               bootstrapBuilder.add(InstallRubyGems.builder().build());
-               bootstrapBuilder.add(ChefSolo.builder() //
-                     .cookbookPath("/var/chef/cookbooks") //
-                     .runlist(RunList.builder().recipes(recipeList).build()) //
-                     .build());
-
-               // Build the statement that will perform all the operations above
-               StatementList bootstrap = new StatementList(bootstrapBuilder.build());
-
-               // Run the script in the nodes of the group
-               runScriptOnGroup(compute, login, groupName, bootstrap);
-               break;
-            case CHEF:
-               // Create the connection to the Chef server
-               ChefService chef = initChefService(System.getProperty("chef.client"),
-                     System.getProperty("chef.validator"));
-
-               // Build the runlist for the deployed nodes
-               System.out.println("Configuring node runlist in the Chef server...");
-               List<String> runlist = new RunListBuilder().addRecipes(recipes.split(",")).build();
-               chef.updateRunListForGroup(runlist, groupName);
-               Statement chefServerBootstrap = chef.createBootstrapScriptForGroup(groupName);
-
-               // Run the script in the nodes of the group
-               System.out.printf(">> installing [%s] on group %s as %s%n", recipes, groupName, login.identity);
-               runScriptOnGroup(compute, login, groupName, chefServerBootstrap);
-               break;
-            case DESTROY:
-               System.out.printf(">> destroying nodes in group %s%n", groupName);
-               // you can use predicates to select which nodes you wish to
-               // destroy.
-               Set<? extends NodeMetadata> destroyed = compute.destroyNodesMatching(//
-                     Predicates.<NodeMetadata> and(not(TERMINATED), inGroup(groupName)));
-               System.out.printf("<< destroyed nodes %s%n", destroyed);
-               break;
-         }
-      } catch (RunNodesException e) {
-         System.err.println("error adding node to group " + groupName + ": " + e.getMessage());
-         error = 1;
-      } catch (RunScriptOnNodesException e) {
-         System.err.println("error installing " + recipes + " on group " + groupName + ": " + e.getMessage());
-         error = 1;
-      } catch (Exception e) {
-         System.err.println("error: " + e.getMessage());
-         error = 1;
-      } finally {
-         compute.getContext().close();
-         System.exit(error);
-      }
-   }
-
-   static int error = 0;
-
-   private static void runScriptOnGroup(ComputeService compute, LoginCredentials login, String groupName,
-         Statement command) throws RunScriptOnNodesException {
-      // when you run commands, you can pass options to decide whether
-      // to run it as root, supply or own credentials vs from cache,
-      // and wrap in an init script vs directly invoke
-      Map<? extends NodeMetadata, ExecResponse> execResponses = compute.runScriptOnNodesMatching(//
-            inGroup(groupName), // predicate used to select nodes
-            command, // what you actually intend to run
-            overrideLoginCredentials(login) // use the local user & ssh key
-                  .runAsRoot(false)); // don't attempt to run as root (sudo)
-
-      for (Entry<? extends NodeMetadata, ExecResponse> response : execResponses.entrySet()) {
-         System.out.printf("<< node %s: %s%n", response.getKey().getId(),
-               concat(response.getKey().getPrivateAddresses(), response.getKey().getPublicAddresses()));
-         System.out.printf("<<     %s%n", response.getValue());
-      }
-   }
-
-   private static ComputeService initComputeService(String provider, String identity, String credential) {
-
-      // example of specific properties, in this case optimizing image list to
-      // only amazon supplied
-      Properties properties = new Properties();
-      properties.setProperty(PROPERTY_EC2_AMI_QUERY, "owner-id=137112412989;state=available;image-type=machine");
-      properties.setProperty(PROPERTY_EC2_CC_AMI_QUERY, "");
-      long scriptTimeout = TimeUnit.MILLISECONDS.convert(20, TimeUnit.MINUTES);
-      properties.setProperty(TIMEOUT_SCRIPT_COMPLETE, scriptTimeout + "");
-
-      // example of injecting a ssh implementation
-      Iterable<Module> modules = ImmutableSet.<Module> of(new SshjSshClientModule(), new SLF4JLoggingModule(),
-            new EnterpriseConfigurationModule());
-
-      ContextBuilder builder = ContextBuilder.newBuilder(provider).credentials(identity, credential).modules(modules)
-            .overrides(properties);
-
-      System.out.printf(">> initializing %s%n", builder.getApiMetadata());
-
-      return builder.buildView(ComputeServiceContext.class).getComputeService();
-   }
-
-   private static ChefService initChefService(String client, String validator) {
-      try {
-         Properties chefConfig = new Properties();
-         chefConfig.put(ChefProperties.CHEF_VALIDATOR_NAME, validator);
-         chefConfig.put(ChefProperties.CHEF_VALIDATOR_CREDENTIAL, credentialForClient(validator));
-
-         ContextBuilder builder = ContextBuilder.newBuilder(new ChefApiMetadata()) //
-               .credentials(client, credentialForClient(client)) //
-               .modules(ImmutableSet.<Module> of(new SLF4JLoggingModule())) //
-               .overrides(chefConfig); //
-
-         System.out.printf(">> initializing %s%n", builder.getApiMetadata());
-
-         ChefContext context = builder.build();
-         return context.getChefService();
-      } catch (Exception e) {
-         System.err.println("error reading private key " + e.getMessage());
-         System.exit(1);
-         return null;
-      }
-   }
-
-   private static LoginCredentials getLoginForCommandExecution(Action action) {
-      try {
-         String user = System.getProperty("user.name");
-         String privateKey = Files.toString(new File(System.getProperty("user.home") + "/.ssh/id_rsa"), UTF_8);
-         return LoginCredentials.builder().user(user).privateKey(privateKey).build();
-      } catch (Exception e) {
-         System.err.println("error reading ssh key " + e.getMessage());
-         System.exit(1);
-         return null;
-      }
-   }
-
-   private static String credentialForClient(final String client) throws Exception {
-      String pemFile = System.getProperty("user.home") + "/.chef/" + client + ".pem";
-      return Files.toString(new File(pemFile), UTF_8);
-   }
-
-}