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);
- }
-
-}