diff --git a/apis/chef/src/main/java/org/jclouds/chef/ChefService.java b/apis/chef/src/main/java/org/jclouds/chef/ChefService.java index a3a434f3fa..c78899aa42 100644 --- a/apis/chef/src/main/java/org/jclouds/chef/ChefService.java +++ b/apis/chef/src/main/java/org/jclouds/chef/ChefService.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.List; +import org.jclouds.chef.domain.BootstrapConfig; import org.jclouds.chef.domain.Client; import org.jclouds.chef.domain.CookbookVersion; import org.jclouds.chef.domain.Environment; @@ -74,22 +75,15 @@ public interface ChefService { Statement createBootstrapScriptForGroup(String group); /** - * assigns a run list to all nodes bootstrapped with a certain group - * - * @param runList - * list of recipes or roles to assign. syntax is - * {@code recipe[name]} and {@code role[name]} + * Configures how the nodes of a certain group will be bootstrapped * * @param group - * corresponds to a configured - * {@link org.jclouds.chef.config.ChefProperties#CHEF_BOOTSTRAP_DATABAG - * databag} where run_list and other information are stored - * @deprecated use - * {@link ChefService#updateBootstrapConfigForGroup(Iterable, String) - + * The group where the given bootstrap configuration will be + * applied. + * @param bootstrapConfig + * The configuration to be applied to the nodes in the group. */ - @Deprecated - void updateRunListForGroup(Iterable runList, String group); + void updateBootstrapConfigForGroup(String group, BootstrapConfig bootstrapConfig); /** * assigns a run list to all nodes bootstrapped with a certain group @@ -102,9 +96,11 @@ public interface ChefService { * corresponds to a configured * {@link org.jclouds.chef.config.ChefProperties#CHEF_BOOTSTRAP_DATABAG * databag} where run_list and other information are stored - * @see #makeChefApiBootstrapScriptForTag + * @deprecated Use {link + * {@link #updateBootstrapConfigForGroup(String, BootstrapConfig)} */ - public void updateBootstrapConfigForGroup(Iterable runList, String group); + @Deprecated + void updateBootstrapConfigForGroup(Iterable runList, String group); /** * assigns a run list to all nodes bootstrapped with a certain group, and @@ -123,9 +119,11 @@ public interface ChefService { * corresponds to a configured * {@link org.jclouds.chef.config.ChefProperties#CHEF_BOOTSTRAP_DATABAG * databag} where run_list and other information are stored - * @see #makeChefApiBootstrapScriptForTag + * @deprecated Use {link + * {@link #updateBootstrapConfigForGroup(String, BootstrapConfig)} */ - public void updateBootstrapConfigForGroup(Iterable runList, JsonBall jsonAttributes, String group); + @Deprecated + void updateBootstrapConfigForGroup(Iterable runList, JsonBall jsonAttributes, String group); /** * @param group diff --git a/apis/chef/src/main/java/org/jclouds/chef/domain/BootstrapConfig.java b/apis/chef/src/main/java/org/jclouds/chef/domain/BootstrapConfig.java new file mode 100644 index 0000000000..6d00ff2f1a --- /dev/null +++ b/apis/chef/src/main/java/org/jclouds/chef/domain/BootstrapConfig.java @@ -0,0 +1,99 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.chef.domain; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.addAll; + +import java.util.List; + +import org.jclouds.domain.JsonBall; + +import com.google.common.base.Optional; +import com.google.common.collect.Lists; + +/** + * Configures how the nodes in a group will bootstrap. + * + * @author Ignasi Barrera + * @since 1.7 + */ +public class BootstrapConfig { + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private List runList = Lists.newArrayList(); + private String environment; + private JsonBall attribtues; + + /** + * Sets the run list that will be executed in the nodes of the group. + */ + public Builder runList(Iterable runList) { + addAll(this.runList, checkNotNull(runList, "runList")); + return this; + } + + /** + * Sets the environment where the nodes in the group will be deployed. + */ + public Builder environment(String environment) { + this.environment = checkNotNull(environment, "environment"); + return this; + } + + /** + * Sets the attributes that will be populated to the deployed nodes. + */ + public Builder attributes(JsonBall attributes) { + this.attribtues = checkNotNull(attributes, "attributes"); + return this; + } + + public BootstrapConfig build() { + return new BootstrapConfig(runList, Optional.fromNullable(environment), Optional.fromNullable(attribtues)); + } + } + + private List runList = Lists.newArrayList(); + private Optional environment; + private Optional attribtues; + + protected BootstrapConfig(List runList, Optional environment, Optional attribtues) { + this.runList = checkNotNull(runList, "runList"); + this.environment = checkNotNull(environment, "environment"); + this.attribtues = checkNotNull(attribtues, "attributes"); + } + + public List getRunList() { + return runList; + } + + public Optional getEnvironment() { + return environment; + } + + public Optional getAttribtues() { + return attribtues; + } + +} diff --git a/apis/chef/src/main/java/org/jclouds/chef/functions/GroupToBootScript.java b/apis/chef/src/main/java/org/jclouds/chef/functions/GroupToBootScript.java index 37baea87de..e619799606 100644 --- a/apis/chef/src/main/java/org/jclouds/chef/functions/GroupToBootScript.java +++ b/apis/chef/src/main/java/org/jclouds/chef/functions/GroupToBootScript.java @@ -44,11 +44,13 @@ import org.jclouds.scriptbuilder.domain.Statement; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; +import com.google.common.base.Joiner; import com.google.common.base.Optional; import com.google.common.base.Splitter; import com.google.common.base.Supplier; import com.google.common.cache.CacheLoader; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.inject.TypeLiteral; /** @@ -82,6 +84,7 @@ public class GroupToBootScript implements Function { this.validatorCredential = checkNotNull(validatorCredential, validatorCredential); } + @Override public Statement apply(String group) { checkNotNull(group, "group"); String validatorClientName = validatorName.get(); @@ -94,6 +97,10 @@ public class GroupToBootScript implements Function { throw propagate(e); } + Map config = json.fromJson(bootstrapConfig.toString(), + BootstrapConfigForGroup.BOOTSTRAP_CONFIG_TYPE); + Optional environment = Optional.fromNullable(config.get("environment")); + String chefConfigDir = "{root}etc{fs}chef"; Statement createChefConfigDir = exec("{md} " + chefConfigDir); Statement createClientRb = appendFile(chefConfigDir + "{fs}client.rb", ImmutableList.of("require 'rubygems'", @@ -106,10 +113,15 @@ public class GroupToBootScript implements Function { Splitter.on('\n').split(Pems.pem(validatorKey))); String chefBootFile = chefConfigDir + "{fs}first-boot.json"; - Statement createFirstBoot = appendFile(chefBootFile, Collections.singleton(json.toJson(bootstrapConfig))); - Statement runChef = exec("chef-client -j " + chefBootFile); + ImmutableMap.Builder options = ImmutableMap.builder(); + options.put("-j", chefBootFile); + if (environment.isPresent()) { + options.put("-E", environment.get().toString()); + } + String strOptions = Joiner.on(' ').withKeyValueSeparator(" ").join(options.build()); + Statement runChef = exec("chef-client " + strOptions); return newStatementList(installChefGems, createChefConfigDir, createClientRb, createValidationPem, createFirstBoot, runChef); diff --git a/apis/chef/src/main/java/org/jclouds/chef/internal/BaseChefService.java b/apis/chef/src/main/java/org/jclouds/chef/internal/BaseChefService.java index b1532a40bc..3e4611c3ff 100644 --- a/apis/chef/src/main/java/org/jclouds/chef/internal/BaseChefService.java +++ b/apis/chef/src/main/java/org/jclouds/chef/internal/BaseChefService.java @@ -36,6 +36,7 @@ import org.jclouds.chef.ChefApi; 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.domain.Client; import org.jclouds.chef.domain.CookbookVersion; import org.jclouds.chef.domain.DatabagItem; @@ -63,10 +64,8 @@ import org.jclouds.logging.Logger; import org.jclouds.scriptbuilder.domain.Statement; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Supplier; -import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.io.ByteStreams; import com.google.common.io.InputSupplier; @@ -210,26 +209,15 @@ public class BaseChefService implements ChefService { } @Override - @Deprecated - public void updateRunListForGroup(Iterable runList, String group) { - updateBootstrapConfigForGroup(runList, group); - } - - @Override - public void updateBootstrapConfigForGroup(Iterable runList, String group) { - updateBootstrapConfigForGroup(runList, null, group); - } - - @Override - public void updateBootstrapConfigForGroup(Iterable runList, @Nullable JsonBall jsonAttributes, String group) { + public void updateBootstrapConfigForGroup(String group, BootstrapConfig bootstrapConfig) { try { api.createDatabag(databag); } catch (IllegalStateException e) { } - String bootstrapConfig = buildBootstrapConfiguration(runList, Optional.fromNullable(jsonAttributes)); - DatabagItem runlist = new DatabagItem(group, bootstrapConfig); + String jsonConfig = buildBootstrapConfiguration(bootstrapConfig); + DatabagItem runlist = new DatabagItem(group, jsonConfig); if (api.getDatabagItem(databag, group) == null) { api.createDatabagItem(databag, runlist); @@ -238,6 +226,19 @@ public class BaseChefService implements ChefService { } } + @Override + @Deprecated + public void updateBootstrapConfigForGroup(Iterable runList, String group) { + updateBootstrapConfigForGroup(runList, null, group); + } + + @Override + @Deprecated + public void updateBootstrapConfigForGroup(Iterable runList, @Nullable JsonBall jsonAttributes, String group) { + updateBootstrapConfigForGroup(group, BootstrapConfig.builder().runList(runList).attributes(jsonAttributes) + .build()); + } + @Override public List getRunListForGroup(String group) { return runListForGroup.apply(group); @@ -261,18 +262,23 @@ public class BaseChefService implements ChefService { } @VisibleForTesting - String buildBootstrapConfiguration(Iterable runList, Optional jsonAttributes) { - checkNotNull(runList, "runList must not be null"); - checkNotNull(jsonAttributes, "jsonAttributes must not be null"); + String buildBootstrapConfiguration(BootstrapConfig bootstrapConfig) { + checkNotNull(bootstrapConfig, "bootstrapConfig must not be null"); - Map bootstrapConfig = Maps.newHashMap(); - bootstrapConfig.put("run_list", Lists.newArrayList(runList)); - if (jsonAttributes.isPresent()) { - Map attributes = json.fromJson(jsonAttributes.get().toString(), - BootstrapConfigForGroup.BOOTSTRAP_CONFIG_TYPE); - bootstrapConfig.putAll(attributes); + Map configMap = Maps.newHashMap(); + configMap.put("run_list", bootstrapConfig.getRunList()); + + if (bootstrapConfig.getEnvironment().isPresent()) { + configMap.put("environment", bootstrapConfig.getEnvironment().get()); } - return json.toJson(bootstrapConfig); + + if (bootstrapConfig.getAttribtues().isPresent()) { + Map attributes = json.fromJson(bootstrapConfig.getAttribtues().get().toString(), + BootstrapConfigForGroup.BOOTSTRAP_CONFIG_TYPE); + configMap.putAll(attributes); + } + + return json.toJson(configMap); } @Override @@ -289,4 +295,5 @@ public class BaseChefService implements ChefService { public Iterable listEnvironmentsNamed(Iterable names) { return listEnvironments.execute(names); } + } diff --git a/apis/chef/src/test/java/org/jclouds/chef/functions/GroupToBootScriptTest.java b/apis/chef/src/test/java/org/jclouds/chef/functions/GroupToBootScriptTest.java index fbaee2569a..482fde84d9 100644 --- a/apis/chef/src/test/java/org/jclouds/chef/functions/GroupToBootScriptTest.java +++ b/apis/chef/src/test/java/org/jclouds/chef/functions/GroupToBootScriptTest.java @@ -42,6 +42,7 @@ import org.jclouds.rest.annotations.ApiVersion; import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.ShellToken; import org.jclouds.scriptbuilder.domain.Statement; +import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import com.google.common.base.Charsets; @@ -63,22 +64,29 @@ import com.google.inject.name.Names; @Test(groups = "unit", testName = "GroupToBootScriptTest") public class GroupToBootScriptTest { - Injector injector = Guice.createInjector(new AbstractModule() { - @Override - protected void configure() { - bind(String.class).annotatedWith(ApiVersion.class).toInstance(ChefApi.VERSION); - bind(String.class).annotatedWith(Names.named(CHEF_UPDATE_GEM_SYSTEM)).toInstance("true"); - bind(String.class).annotatedWith(Names.named(CHEF_UPDATE_GEMS)).toInstance("true"); - } - }, new ChefParserModule(), new GsonModule(), new ChefBootstrapModule()); + private Json json; + private Statement installChefGems; + private Optional validatorName; - Json json = injector.getInstance(Json.class); - Statement installChefGems = injector.getInstance(Key.get(Statement.class, Names.named("installChefGems"))); - Optional validatorName = Optional. of("chef-validator"); - Optional validatorCredential = Optional. of(createMock(PrivateKey.class)); + @BeforeClass + public void setup() { + Injector injector = Guice.createInjector(new AbstractModule() { + @Override + protected void configure() { + bind(String.class).annotatedWith(ApiVersion.class).toInstance(ChefApi.VERSION); + bind(String.class).annotatedWith(Names.named(CHEF_UPDATE_GEM_SYSTEM)).toInstance("true"); + bind(String.class).annotatedWith(Names.named(CHEF_UPDATE_GEMS)).toInstance("true"); + } + }, new ChefParserModule(), new GsonModule(), new ChefBootstrapModule()); + + json = injector.getInstance(Json.class); + installChefGems = injector.getInstance(Key.get(Statement.class, Names.named("installChefGems"))); + validatorName = Optional. of("chef-validator"); + } @Test(expectedExceptions = IllegalStateException.class) public void testMustHaveValidatorName() { + Optional validatorCredential = Optional.of(createMock(PrivateKey.class)); GroupToBootScript fn = new GroupToBootScript(Suppliers.ofInstance(URI.create("http://localhost:4000")), json, CacheLoader.from(Functions.forMap(ImmutableMap. of())), installChefGems, Optional. absent(), validatorCredential); @@ -95,6 +103,7 @@ public class GroupToBootScriptTest { @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "Key 'foo' not present in map") public void testMustHaveRunScriptsName() { + Optional validatorCredential = Optional.of(createMock(PrivateKey.class)); GroupToBootScript fn = new GroupToBootScript(Suppliers.ofInstance(URI.create("http://localhost:4000")), json, CacheLoader.from(Functions.forMap(ImmutableMap. of())), installChefGems, validatorName, validatorCredential); @@ -103,6 +112,7 @@ public class GroupToBootScriptTest { @Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "null value in entry: foo=null") public void testMustHaveRunScriptsValue() { + Optional validatorCredential = Optional.of(createMock(PrivateKey.class)); GroupToBootScript fn = new GroupToBootScript(Suppliers.ofInstance(URI.create("http://localhost:4000")), json, CacheLoader.from(Functions.forMap(ImmutableMap. of("foo", (DatabagItem) null))), installChefGems, validatorName, validatorCredential); @@ -110,6 +120,7 @@ public class GroupToBootScriptTest { } public void testOneRecipe() throws IOException { + Optional validatorCredential = Optional.of(createMock(PrivateKey.class)); GroupToBootScript fn = new GroupToBootScript(Suppliers.ofInstance(URI.create("http://localhost:4000")), json, CacheLoader.from(Functions.forMap(ImmutableMap. of("foo", new JsonBall( "{\"tomcat6\":{\"ssl_port\":8433},\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"]}")))), @@ -131,4 +142,29 @@ public class GroupToBootScriptTest { verify(validatorKey); } + + public void testOneRecipeAndEnvironment() throws IOException { + Optional validatorCredential = Optional.of(createMock(PrivateKey.class)); + GroupToBootScript fn = new GroupToBootScript(Suppliers.ofInstance(URI.create("http://localhost:4000")), json, + CacheLoader.from(Functions.forMap(ImmutableMap. of("foo", new JsonBall( + "{\"tomcat6\":{\"ssl_port\":8433},\"environment\":\"env\"," + + "\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"]}")))), installChefGems, + validatorName, validatorCredential); + + PrivateKey validatorKey = validatorCredential.get(); + expect(validatorKey.getEncoded()).andReturn(PemsTest.PRIVATE_KEY.getBytes()); + replay(validatorKey); + + assertEquals( + fn.apply("foo").render(OsFamily.UNIX), + Resources.toString(Resources.getResource("test_install_ruby." + ShellToken.SH.to(OsFamily.UNIX)), + Charsets.UTF_8) + + Resources.toString( + Resources.getResource("test_install_rubygems." + ShellToken.SH.to(OsFamily.UNIX)), + Charsets.UTF_8) + + "gem install chef --no-rdoc --no-ri\n" + + Resources.toString(Resources.getResource("bootstrap-env.sh"), Charsets.UTF_8)); + + verify(validatorKey); + } } diff --git a/apis/chef/src/test/java/org/jclouds/chef/internal/BaseChefServiceTest.java b/apis/chef/src/test/java/org/jclouds/chef/internal/BaseChefServiceTest.java index 65fb31d10e..46a3f8d036 100644 --- a/apis/chef/src/test/java/org/jclouds/chef/internal/BaseChefServiceTest.java +++ b/apis/chef/src/test/java/org/jclouds/chef/internal/BaseChefServiceTest.java @@ -24,6 +24,7 @@ import java.util.List; import org.jclouds.ContextBuilder; import org.jclouds.chef.ChefApiMetadata; +import org.jclouds.chef.domain.BootstrapConfig; import org.jclouds.chef.filters.SignedHeaderAuthTest; import org.jclouds.chef.util.RunListBuilder; import org.jclouds.domain.JsonBall; @@ -32,7 +33,6 @@ import org.jclouds.rest.internal.BaseRestApiTest.MockModule; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.inject.Injector; @@ -57,39 +57,47 @@ public class BaseChefServiceTest { chefService = injector.getInstance(BaseChefService.class); } - @Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "runList must not be null") - public void testBuildBootstrapConfigurationWithNullRunlist() { - chefService.buildBootstrapConfiguration(null, null); - } - - @Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "jsonAttributes must not be null") - public void testBuildBootstrapConfigurationWithNullJsonAttributes() { - chefService.buildBootstrapConfiguration(ImmutableList. of(), null); + @Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "bootstrapConfig must not be null") + public void testBuildBootstrapConfigurationWithNullConfig() { + chefService.buildBootstrapConfiguration(null); } public void testBuildBootstrapConfigurationWithEmptyRunlist() { - String config = chefService - .buildBootstrapConfiguration(ImmutableList. of(), Optional. absent()); + BootstrapConfig bootstrapConfig = BootstrapConfig.builder().runList(ImmutableList. of()).build(); + String config = chefService.buildBootstrapConfiguration(bootstrapConfig); assertEquals(config, "{\"run_list\":[]}"); } public void testBuildBootstrapConfigurationWithRunlist() { List runlist = new RunListBuilder().addRecipe("apache2").addRole("webserver").build(); - String config = chefService.buildBootstrapConfiguration(runlist, Optional. absent()); + BootstrapConfig bootstrapConfig = BootstrapConfig.builder().runList(runlist).build(); + String config = chefService.buildBootstrapConfiguration(bootstrapConfig); assertEquals(config, "{\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"]}"); } public void testBuildBootstrapConfigurationWithRunlistAndEmptyAttributes() { List runlist = new RunListBuilder().addRecipe("apache2").addRole("webserver").build(); - String config = chefService.buildBootstrapConfiguration(runlist, Optional.of(new JsonBall("{}"))); + BootstrapConfig bootstrapConfig = BootstrapConfig.builder().runList(runlist).attributes(new JsonBall("{}")) + .build(); + String config = chefService.buildBootstrapConfiguration(bootstrapConfig); assertEquals(config, "{\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"]}"); } public void testBuildBootstrapConfigurationWithRunlistAndAttributes() { List runlist = new RunListBuilder().addRecipe("apache2").addRole("webserver").build(); - String config = chefService.buildBootstrapConfiguration(runlist, - Optional.of(new JsonBall("{\"tomcat6\":{\"ssl_port\":8433}}"))); + BootstrapConfig bootstrapConfig = BootstrapConfig.builder().runList(runlist) + .attributes(new JsonBall("{\"tomcat6\":{\"ssl_port\":8433}}")).build(); + String config = chefService.buildBootstrapConfiguration(bootstrapConfig); assertEquals(config, "{\"tomcat6\":{\"ssl_port\":8433},\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"]}"); } + public void testBuildBootstrapConfigurationWithRunlistAndAttributesAndEnvironment() { + List runlist = new RunListBuilder().addRecipe("apache2").addRole("webserver").build(); + BootstrapConfig bootstrapConfig = BootstrapConfig.builder().runList(runlist) + .attributes(new JsonBall("{\"tomcat6\":{\"ssl_port\":8433}}")).environment("env").build(); + String config = chefService.buildBootstrapConfiguration(bootstrapConfig); + assertEquals(config, + "{\"tomcat6\":{\"ssl_port\":8433},\"environment\":\"env\",\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"]}"); + } + } diff --git a/apis/chef/src/test/resources/bootstrap-env.sh b/apis/chef/src/test/resources/bootstrap-env.sh new file mode 100755 index 0000000000..315e24889b --- /dev/null +++ b/apis/chef/src/test/resources/bootstrap-env.sh @@ -0,0 +1,56 @@ +mkdir -p /etc/chef +cat >> /etc/chef/client.rb <<-'END_OF_JCLOUDS_FILE' + require 'rubygems' + require 'ohai' + o = Ohai::System.new + o.all_plugins + node_name "foo-" + o[:ipaddress] + log_level :info + log_location STDOUT + validation_client_name "chef-validator" + chef_server_url "http://localhost:4000" +END_OF_JCLOUDS_FILE +cat >> /etc/chef/validation.pem <<-'END_OF_JCLOUDS_FILE' + -----BEGIN PRIVATE KEY----- + LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVB + eWIyWkpKcUdtMEtLUis4bmZRSk5zU2QrRjl0WE5NVjdDZk9jVzZqc3FzOEVaZ2lW + ClIwOWhEMUlZT2o0WXFNMHFKT05sZ3lnNHhSV2V3ZFNHN1FUUGoxbEpwVkFpZGE5 + c1h5MitrenlhZ1pBMUFtME8KWmNicWI1aG9lSURnY1grZURhNzlzMHUwRG9tamNm + TzlFS2h2SExCeit6TSszUXFQUmtQVjhuWVRiZnMrSGpWegp6T1U2RDFCMFhSMytJ + UFpabDJBbldzMmQwcWhuU3RIY0RVdm5SVlEwUDQ4Mll3TjlWZ2NlT1p0cFB6MERD + S0VKCjVUeDVTVHViOGswL3p0L1ZBTUhRYWZMU3VRTUxkMnM0Wkx1T1pwdE4vL3VB + c1RteGlyZXFkMzd6KzhaVGRCYkoKOExFcEoraUNYdVNmbTVhVWg3aXc2b3h2VG9Z + MkFMNTMraksyVVFJREFRQUJBb0lCQVFEQTg4QjNpL3hXbjB2WApCVnhGYW1DWW9l + Y3VOakd3WFhrU3laZXc2MTZBK0VPQ3U0N2JoNGFUdXJkRmJZTDBZRmFBdGFXdnps + YU4yZUhnCkRiK0hEdVRlZkUyOStXa2NHazZTc2hQbWl6NVQwWE9DQUlDV3c2d1NW + RGtIbUd3UzRqWnZiQUZtN1c4bndHazkKWWh4Z3hGaVJuZ3N3SlpGb3BPTG9GNVdY + czJ0ZDhndUlZTnNsTXBvN3R1NTBpRm5CSHdLTzJac1BBazh0OW5uUwp4bERhdkty + dXltRW1xSENyMytkdGlvNWVhZW5KY3AzZmpvWEJRT0tVazNpcElJMjlYUkI4TnFl + Q1ZWLzdLeHdxCmNrcU9CRWJSd0JjbGNreUliRCtSaUFnS3ZPZWxPUmpFaUU5UjQy + dnVxdnhSQTZrOWtkOW83dXRsWDBBVXRwRW4KM2daYzZMZXBBb0dCQVA5YWVsNVk3 + NStzSzJKSlVOT09oTzhhZTQ1Y2RzaWxwMnlJMFgrVUJhU3VRczIrZHlQcAprcEVI + QXhkNHBtbVN2bi84YzlUbEVaaHIrcVliQUJYVlBsRG5jeHBJdXcyQWpiazdzL1M0 + WGFTS3NScXBYTDU3CnpqL1FPcUxrUms4K09WVjlxNmxNZVFOcUx0RWoxdTZKUHZp + WDcwUm8rRlF0UnR0Tk9ZYmZkUC9mQW9HQkFNcEEKWGpSNXdvVjVzVWIrUkVnOXZF + dVlvOFJTeU9hcnhxS0ZDSVhWVU5zTE94KzIyK0FLNCtDUXBidWVXTjdqb3RybApZ + RDZ1VDZzdldpM0FBQzdraVkwVUkvZmpWUFJDVWk4dFZvUVVFMFRhVTVWTElUYVlP + QitXL2JCYURFNE05NTYwCjFOdURXTzkwYmFBNWRmVTQ0aXV6dmEwMnJHSlhLOStu + UzNvOG5rL1BBb0dCQUxPTDZkam5EZTRtd0FhRzZKY28KY2Q0eHI4amt5UHpDUlp1 + eUJDU0Jid3BoSVVYTGM3aERwclBreTA2NG5jSkQxVURtd0lka1hkL2ZwTWtnMlFt + QQovQ1VrNkxFRmpNaXNxSG9qT2FDTDlnUVpKUGhMTjVRVU4yeDFQSldHanMxdlFo + OFRreDBpVVVDT2E4YlFQWE5SCiszNE9Uc1c2VFVuYTRDU1pBeWNMZmhmZkFvR0JB + SWdnVnNlZkJDdnVRa0YwTmVVaG1EQ1JaZmhuZDh5NTVSSFIKMUhDdnFLSWxwdity + aGNYL3pteUJMdXRlb3BZeVJKUnNPaUUyRlcwMGk4K3JJUFJ1NFozUTVueWJ4N3cz + UHpWOQpvSE41UjViYUU5T3lJNEtwWld6dHBZWWl0WkY2N05jbkF2VlVMSEhPdlZK + UUduS1lmTEhKWW1ySkY3R0Exb2pNCkF1TWRGYmpGQW9HQVB4VWh4d0Z5OGdhcUJh + aEtVRVpuNEY4MUhGUDVpaEdoa1Q0UUw2QUZQTzJlK0poSUdqdVIKMjcrODVoY0Zx + UStISFZ0RnNtODFiL2ErUjdQNFV1Q1JnYzhlQ2p4UU1vSjFYbDRuN1ZialBiSE1u + SU4wUnl2ZApPNFpwV0RXWW5DTzAyMUpUT1VVT0o0Si95MDQxNkJ2a3cwejU5eTdz + Tlg3d0RCQkhIYksvWENjPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo= + -----END PRIVATE KEY----- + +END_OF_JCLOUDS_FILE +cat >> /etc/chef/first-boot.json <<-'END_OF_JCLOUDS_FILE' + {"tomcat6":{"ssl_port":8433},"environment":"env","run_list":["recipe[apache2]","role[webserver]"]} +END_OF_JCLOUDS_FILE +chef-client -j /etc/chef/first-boot.json -E "env"