Updated Branches: refs/heads/master c96412771 -> d7ed1bf09
Updated to 1.7.0 and to use the Omnibus installer in Solo Project: http://git-wip-us.apache.org/repos/asf/jclouds-examples/repo Commit: http://git-wip-us.apache.org/repos/asf/jclouds-examples/commit/d7ed1bf0 Tree: http://git-wip-us.apache.org/repos/asf/jclouds-examples/tree/d7ed1bf0 Diff: http://git-wip-us.apache.org/repos/asf/jclouds-examples/diff/d7ed1bf0 Branch: refs/heads/master Commit: d7ed1bf0992a7960bc53b2755c5ea62acf607418 Parents: c964127 Author: Ignasi Barrera <[email protected]> Authored: Thu Jan 23 16:17:37 2014 +0100 Committer: Ignasi Barrera <[email protected]> Committed: Thu Jan 23 16:17:37 2014 +0100 ---------------------------------------------------------------------- chef-basics/pom.xml | 40 +-- .../jclouds/examples/chef/basics/MainApp.java | 359 +++++++++++++++++++ .../jclouds/examples/chef/basics/MainApp.java | 314 ---------------- 3 files changed, 374 insertions(+), 339 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/jclouds-examples/blob/d7ed1bf0/chef-basics/pom.xml ---------------------------------------------------------------------- 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 @@ 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/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 @@ under the License. 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 @@ under the License. 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 @@ under the License. <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 @@ under the License. </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 @@ under the License. <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 @@ under the License. </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> http://git-wip-us.apache.org/repos/asf/jclouds-examples/blob/d7ed1bf0/chef-basics/src/main/java/org/apache/jclouds/examples/chef/basics/MainApp.java ---------------------------------------------------------------------- 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); + } + +} http://git-wip-us.apache.org/repos/asf/jclouds-examples/blob/d7ed1bf0/chef-basics/src/main/java/org/jclouds/examples/chef/basics/MainApp.java ---------------------------------------------------------------------- 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); - } - -}
