diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/chef/Role.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/chef/Role.java new file mode 100644 index 0000000000..5de651b4b4 --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/chef/Role.java @@ -0,0 +1,193 @@ +/** + * 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.scriptbuilder.domain.chef; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.transform; + +import java.util.List; + +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.base.Objects; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +/** + * A Role to be configured for a Chef Solo run. + * + * @author Ignasi Barrera + */ +public class Role { + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String name; + private String description; + private String jsonDefaultAttributes; + private String jsonOverrideAttributes; + private List runlist = Lists.newArrayList(); + + public Builder name(String name) { + this.name = checkNotNull(name, "name must be set"); + return this; + } + + public Builder description(String description) { + this.description = checkNotNull(description, "description must be set"); + return this; + } + + public Builder jsonDefaultAttributes(String jsonDefaultAttributes) { + this.jsonDefaultAttributes = checkNotNull(jsonDefaultAttributes, "jsonDefaultAttributes must be set"); + return this; + } + + public Builder jsonOverrideAttributes(String jsonOverrideAttributes) { + this.jsonOverrideAttributes = checkNotNull(jsonOverrideAttributes, "jsonOverrideAttributes must be set"); + return this; + } + + public Builder installRecipe(String recipe) { + this.runlist.add("recipe[" + checkNotNull(recipe, "recipe must be set") + "]"); + return this; + } + + public Builder installRecipes(Iterable recipes) { + this.runlist.addAll(Lists.newArrayList(transform(checkNotNull(recipes, "recipes must be set"), + new Function() { + @Override + public String apply(String input) { + return "recipe[" + input + "]"; + } + }))); + return this; + } + + public Builder installRole(String role) { + this.runlist.add("role[" + checkNotNull(role, "role must be set") + "]"); + return this; + } + + public Builder installRoles(Iterable roles) { + this.runlist.addAll(Lists.newArrayList(transform(checkNotNull(roles, "roles must be set"), + new Function() { + @Override + public String apply(String input) { + return "role[" + input + "]"; + } + }))); + return this; + } + + public Role build() { + return new Role(name, Optional.fromNullable(description), Optional.fromNullable(jsonDefaultAttributes), + Optional.fromNullable(jsonOverrideAttributes), runlist); + } + } + + private String name; + private Optional description; + private Optional jsonDefaultAttributes; + private Optional jsonOverrideAttributes; + private List runlist; + + protected Role(String name, Optional description, Optional jsonDefaultAttributes, + Optional jsonOverrideAttributes, List runlist) { + this.name = checkNotNull(name, "name must be set"); + this.description = checkNotNull(description, "description must be set"); + this.jsonDefaultAttributes = checkNotNull(jsonDefaultAttributes, "jsonDefaultAttributes must be set"); + this.jsonOverrideAttributes = checkNotNull(jsonOverrideAttributes, "jsonOverrideAttributes must be set"); + this.runlist = ImmutableList. copyOf(checkNotNull(runlist, "runlist must be set")); + } + + public String toJsonString() { + StringBuilder json = new StringBuilder(); + json.append("{"); + json.append("\"name\": \"").append(name).append("\","); + json.append("\"description\":\"").append(description.or("")).append("\","); + json.append("\"default_attributes\":").append(jsonDefaultAttributes.or("{}")).append(","); + json.append("\"override_attributes\":").append(jsonOverrideAttributes.or("{}")).append(","); + json.append("\"json_class\":\"Chef::Role\","); + json.append("\"chef_type\":\"role\","); + json.append("\"run_list\":[").append(runlistToJsonString(runlist)).append("]"); + json.append("}"); + return json.toString(); + } + + public String getName() { + return name; + } + + public Optional getDescription() { + return description; + } + + public Optional getJsonDefaultAttributes() { + return jsonDefaultAttributes; + } + + public Optional getJsonOverrideAttributes() { + return jsonOverrideAttributes; + } + + public List getRunlist() { + return runlist; + } + + @Override + public int hashCode() { + return Objects.hashCode(name); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Role other = Role.class.cast(obj); + return Objects.equal(name, other.name); + } + + @Override + public String toString() { + return Objects.toStringHelper(this).omitNullValues().add("name", name).add("description", description.orNull()) + .toString(); + } + + private static String runlistToJsonString(List runlist) { + return Joiner.on(',').join(transform(runlist, new Function() { + @Override + public String apply(String input) { + return "\"" + input + "\""; + } + })); + } + +} diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/chef/ChefSolo.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/chef/ChefSolo.java index 9da2eeb55d..ed902a25b7 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/chef/ChefSolo.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/chef/ChefSolo.java @@ -29,12 +29,14 @@ import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.StatementList; import org.jclouds.scriptbuilder.domain.Statements; +import org.jclouds.scriptbuilder.domain.chef.Role; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; /** @@ -43,6 +45,7 @@ import com.google.common.collect.Lists; * @author Ignasi Barrera */ public class ChefSolo implements Statement { + public static Builder builder() { return new Builder(); } @@ -50,7 +53,8 @@ public class ChefSolo implements Statement { public static class Builder { private String cookbooksArchiveLocation; private String jsonAttributes; - private List recipes = Lists.newArrayList(); + private List roles = Lists.newArrayList(); + private List runlist = Lists.newArrayList(); public Builder cookbooksArchiveLocation(String cookbooksArchiveLocation) { this.cookbooksArchiveLocation = checkNotNull(cookbooksArchiveLocation, "cookbooksArchiveLocation"); @@ -62,30 +66,64 @@ public class ChefSolo implements Statement { return this; } + public Builder defineRole(Role role) { + this.roles.add(checkNotNull(role, "role")); + return this; + } + + public Builder defineRoles(Iterable roles) { + this.roles = ImmutableList. copyOf(checkNotNull(roles, "roles")); + return this; + } + public Builder installRecipe(String recipe) { - this.recipes.add(checkNotNull(recipe, "recipe")); + this.runlist.add("recipe[" + checkNotNull(recipe, "recipe") + "]"); return this; } public Builder installRecipes(Iterable recipes) { - this.recipes = ImmutableList. copyOf(checkNotNull(recipes, "recipes")); + this.runlist.addAll(Lists.newArrayList(transform(checkNotNull(recipes, "recipes"), + new Function() { + @Override + public String apply(String input) { + return "recipe[" + input + "]"; + } + }))); + return this; + } + + public Builder installRole(String role) { + this.runlist.add("role[" + checkNotNull(role, "role") + "]"); + return this; + } + + public Builder installRoles(Iterable roles) { + this.runlist.addAll(Lists.newArrayList(transform(checkNotNull(roles, "roles"), new Function() { + @Override + public String apply(String input) { + return "role[" + input + "]"; + } + }))); return this; } public ChefSolo build() { - return new ChefSolo(cookbooksArchiveLocation, recipes, Optional.fromNullable(jsonAttributes)); + return new ChefSolo(cookbooksArchiveLocation, Optional.fromNullable(jsonAttributes), roles, runlist); } } private String cookbooksArchiveLocation; private Optional jsonAttributes; - private List recipes; + private List roles; + private List runlist; private final InstallChefGems installChefGems = new InstallChefGems(); - public ChefSolo(String cookbooksArchiveLocation, List recipes, Optional jsonAttributes) { + public ChefSolo(String cookbooksArchiveLocation, Optional jsonAttributes, List roles, + List runlist) { this.cookbooksArchiveLocation = checkNotNull(cookbooksArchiveLocation, "cookbooksArchiveLocation must be set"); - this.recipes = ImmutableList.copyOf(checkNotNull(recipes, "recipes must be set")); + this.roles = ImmutableList.copyOf(checkNotNull(roles, "roles must be set")); + this.runlist = ImmutableList.copyOf(checkNotNull(runlist, "runlist must be set")); this.jsonAttributes = checkNotNull(jsonAttributes, "jsonAttributes must be set"); } @@ -97,23 +135,31 @@ public class ChefSolo implements Statement { ImmutableList.Builder statements = ImmutableList.builder(); statements.add(installChefGems); + statements.add(exec("{md} /var/chef")); + + // The roles directory must contain one file for each role definition + if (!roles.isEmpty()) { + statements.add(exec("{md} /var/chef/roles")); + for (Role role : roles) { + statements.add(createOrOverwriteFile("/var/chef/roles/" + role.getName() + ".json", + ImmutableSet.of(role.toJsonString()))); + } + } ImmutableMap.Builder chefSoloOptions = ImmutableMap.builder(); chefSoloOptions.put("-N", "`hostname`"); chefSoloOptions.put("-r", cookbooksArchiveLocation); if (jsonAttributes.isPresent()) { - statements.add(exec("{md} /var/chef")); statements.add(createOrOverwriteFile("/var/chef/node.json", jsonAttributes.asSet())); chefSoloOptions.put("-j", "/var/chef/node.json"); } - if (!recipes.isEmpty()) { - chefSoloOptions.put("-o", recipesToRunlistString(recipes)); + if (!runlist.isEmpty()) { + chefSoloOptions.put("-o", Joiner.on(',').join(runlist)); } String options = Joiner.on(' ').withKeyValueSeparator(" ").join(chefSoloOptions.build()); - statements.add(Statements.exec(String.format("chef-solo %s", options))); return new StatementList(statements.build()).render(family); @@ -124,13 +170,4 @@ public class ChefSolo implements Statement { return installChefGems.functionDependencies(family); } - private static String recipesToRunlistString(List recipes) { - return Joiner.on(',').join(transform(recipes, new Function() { - @Override - public String apply(String input) { - return "recipe[" + input + "]"; - } - })); - } - } diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/chef/RoleTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/chef/RoleTest.java new file mode 100644 index 0000000000..90c293eefa --- /dev/null +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/chef/RoleTest.java @@ -0,0 +1,115 @@ +/** + * 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.scriptbuilder.domain.chef; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; + +/** + * Unit tests for the {@link Role} class. + * + * @author Ignasi Barrera + */ +@Test(groups = "unit", testName = "RoleTest") +public class RoleTest { + + public void testToJsonStringWithOnlyName() { + Role role = Role.builder().name("foo").build(); + assertEquals(role.toJsonString(), "{\"name\": \"foo\",\"description\":\"\",\"default_attributes\":{}," + + "\"override_attributes\":{},\"json_class\":\"Chef::Role\",\"chef_type\":\"role\",\"run_list\":[]}"); + } + + public void testToJsonStringWithDescription() { + Role role = Role.builder().name("foo").description("Foo role").build(); + assertEquals(role.toJsonString(), "{\"name\": \"foo\",\"description\":\"Foo role\",\"default_attributes\":{}," + + "\"override_attributes\":{},\"json_class\":\"Chef::Role\",\"chef_type\":\"role\",\"run_list\":[]}"); + } + + public void testToJsonStringWithDefaultAttributes() { + Role role = Role.builder().name("foo").jsonDefaultAttributes("{\"foo\":\"bar\"}").build(); + assertEquals(role.toJsonString(), + "{\"name\": \"foo\",\"description\":\"\",\"default_attributes\":{\"foo\":\"bar\"}," + + "\"override_attributes\":{},\"json_class\":\"Chef::Role\",\"chef_type\":\"role\"," + + "\"run_list\":[]}"); + } + + public void testToJsonStringWithOverrideAttributes() { + Role role = Role.builder().name("foo").jsonOverrideAttributes("{\"foo\":\"bar\"}").build(); + assertEquals(role.toJsonString(), "{\"name\": \"foo\",\"description\":\"\",\"default_attributes\":{}," + + "\"override_attributes\":{\"foo\":\"bar\"},\"json_class\":\"Chef::Role\",\"chef_type\":\"role\"," + + "\"run_list\":[]}"); + } + + public void testToJsonStringWithSingleRecipe() { + Role role = Role.builder().name("foo").installRecipe("apache2").build(); + assertEquals(role.toJsonString(), + "{\"name\": \"foo\",\"description\":\"\",\"default_attributes\":{},\"override_attributes\":{}," + + "\"json_class\":\"Chef::Role\",\"chef_type\":\"role\",\"run_list\":[\"recipe[apache2]\"]}"); + } + + public void testToJsonStringWithMultipleRecipes() { + Role role = Role.builder().name("foo").installRecipe("apache2").installRecipe("git").build(); + assertEquals(role.toJsonString(), + "{\"name\": \"foo\",\"description\":\"\",\"default_attributes\":{},\"override_attributes\":{}," + + "\"json_class\":\"Chef::Role\",\"chef_type\":\"role\"," + + "\"run_list\":[\"recipe[apache2]\",\"recipe[git]\"]}"); + } + + public void testToJsonStringWithMultipleRecipesInList() { + Role role = Role.builder().name("foo").installRecipes(ImmutableList.of("apache2", "git")).build(); + assertEquals(role.toJsonString(), + "{\"name\": \"foo\",\"description\":\"\",\"default_attributes\":{},\"override_attributes\":{}," + + "\"json_class\":\"Chef::Role\",\"chef_type\":\"role\"," + + "\"run_list\":[\"recipe[apache2]\",\"recipe[git]\"]}"); + } + + public void testToJsonStringWithSingleRole() { + Role role = Role.builder().name("foo").installRole("webserver").build(); + assertEquals(role.toJsonString(), + "{\"name\": \"foo\",\"description\":\"\",\"default_attributes\":{},\"override_attributes\":{}," + + "\"json_class\":\"Chef::Role\",\"chef_type\":\"role\",\"run_list\":[\"role[webserver]\"]}"); + } + + public void testToJsonStringWithMultipleRoles() { + Role role = Role.builder().name("foo").installRole("webserver").installRole("firewall").build(); + assertEquals(role.toJsonString(), + "{\"name\": \"foo\",\"description\":\"\",\"default_attributes\":{},\"override_attributes\":{}," + + "\"json_class\":\"Chef::Role\",\"chef_type\":\"role\"," + + "\"run_list\":[\"role[webserver]\",\"role[firewall]\"]}"); + } + + public void testToJsonStringWithMultipleRolesInList() { + Role role = Role.builder().name("foo").installRoles(ImmutableList.of("webserver", "firewall")).build(); + assertEquals(role.toJsonString(), + "{\"name\": \"foo\",\"description\":\"\",\"default_attributes\":{},\"override_attributes\":{}," + + "\"json_class\":\"Chef::Role\",\"chef_type\":\"role\"," + + "\"run_list\":[\"role[webserver]\",\"role[firewall]\"]}"); + } + + public void testToJsonStringWithRolesAndRecipes() { + Role role = Role.builder().name("foo").installRole("webserver").installRecipe("git").build(); + assertEquals(role.toJsonString(), + "{\"name\": \"foo\",\"description\":\"\",\"default_attributes\":{},\"override_attributes\":{}," + + "\"json_class\":\"Chef::Role\",\"chef_type\":\"role\"," + + "\"run_list\":[\"role[webserver]\",\"recipe[git]\"]}"); + } +} diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/chef/ChefSoloTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/chef/ChefSoloTest.java index 9e9f975028..fd8293dc83 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/chef/ChefSoloTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/chef/ChefSoloTest.java @@ -24,12 +24,11 @@ import java.io.IOException; import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.ShellToken; +import org.jclouds.scriptbuilder.domain.chef.Role; import org.testng.annotations.Test; import com.google.common.base.Charsets; -import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; import com.google.common.io.Resources; @Test(groups = "unit", testName = "ChefSoloTest") @@ -40,16 +39,6 @@ public class ChefSoloTest { ChefSolo.builder().build(); } - @Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "recipes must be set") - public void testChefSoloWithoutRecipes() { - new ChefSolo("/tmp/foo", null, Optional. absent()); - } - - @Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "jsonAttributes must be set") - public void testChefSoloWithoutAttributes() { - new ChefSolo("/tmp/foo", Lists. newArrayList(), null); - } - @Test(expectedExceptions = UnsupportedOperationException.class, expectedExceptionsMessageRegExp = "windows not yet implemented") public void testChefSoloInWindows() { ChefSolo.builder().cookbooksArchiveLocation("/tmp/cookbooks").build().render(OsFamily.WINDOWS); @@ -60,7 +49,8 @@ public class ChefSoloTest { assertEquals( script, Resources.toString(Resources.getResource("test_install_ruby." + ShellToken.SH.to(OsFamily.UNIX)), - Charsets.UTF_8) + "installChefGems || return 1\nchef-solo -N `hostname` -r /tmp/cookbooks\n"); + Charsets.UTF_8) + + "installChefGems || return 1\nmkdir -p /var/chef\nchef-solo -N `hostname` -r /tmp/cookbooks\n"); } public void testChefSoloWithCookbooksLocationAndSingleRecipe() throws IOException { @@ -70,7 +60,8 @@ public class ChefSoloTest { script, Resources.toString(Resources.getResource("test_install_ruby." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8) - + "installChefGems || return 1\nchef-solo -N `hostname` -r /tmp/cookbooks -o recipe[apache2]\n"); + + "installChefGems || return 1\nmkdir -p /var/chef\n" + + "chef-solo -N `hostname` -r /tmp/cookbooks -o recipe[apache2]\n"); } public void testChefSoloWithCookbooksLocationAndMultipleRecipes() throws IOException { @@ -80,7 +71,8 @@ public class ChefSoloTest { script, Resources.toString(Resources.getResource("test_install_ruby." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8) - + "installChefGems || return 1\nchef-solo -N `hostname` -r /tmp/cookbooks -o recipe[apache2],recipe[mysql]\n"); + + "installChefGems || return 1\nmkdir -p /var/chef\n" + + "chef-solo -N `hostname` -r /tmp/cookbooks -o recipe[apache2],recipe[mysql]\n"); } public void testChefSoloWithCookbooksLocationAndMultipleRecipesInList() throws IOException { @@ -90,7 +82,8 @@ public class ChefSoloTest { script, Resources.toString(Resources.getResource("test_install_ruby." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8) - + "installChefGems || return 1\nchef-solo -N `hostname` -r /tmp/cookbooks -o recipe[apache2],recipe[mysql]\n"); + + "installChefGems || return 1\nmkdir -p /var/chef\n" + + "chef-solo -N `hostname` -r /tmp/cookbooks -o recipe[apache2],recipe[mysql]\n"); } public void testChefSoloWithCookbooksLocationAndAttributes() throws IOException { @@ -106,4 +99,48 @@ public class ChefSoloTest { + "chef-solo -N `hostname` -r /tmp/cookbooks -j /var/chef/node.json -o recipe[apache2]\n"); } + public void testChefSoloWithRoleDefinitionAndRecipe() throws IOException { + Role role = Role.builder().name("foo").installRecipe("apache2").build(); + String script = ChefSolo.builder().cookbooksArchiveLocation("/tmp/cookbooks").defineRole(role) + .installRecipe("apache2").build().render(OsFamily.UNIX); + assertEquals( + script, + Resources.toString(Resources.getResource("test_install_ruby." + ShellToken.SH.to(OsFamily.UNIX)), + Charsets.UTF_8) + + "installChefGems || return 1\nmkdir -p /var/chef\nmkdir -p /var/chef/roles\n" + + "cat > /var/chef/roles/foo.json <<-'END_OF_JCLOUDS_FILE'\n" + + "\t{\"name\": \"foo\",\"description\":\"\",\"default_attributes\":{},\"override_attributes\":{}," + + "\"json_class\":\"Chef::Role\",\"chef_type\":\"role\",\"run_list\":[\"recipe[apache2]\"]}" + + "\nEND_OF_JCLOUDS_FILE\nchef-solo -N `hostname` -r /tmp/cookbooks -o recipe[apache2]\n"); + } + + public void testChefSoloWithRoleDefinitionAndRole() throws IOException { + Role role = Role.builder().name("foo").installRecipe("apache2").build(); + String script = ChefSolo.builder().cookbooksArchiveLocation("/tmp/cookbooks").defineRole(role).installRole("foo") + .build().render(OsFamily.UNIX); + assertEquals( + script, + Resources.toString(Resources.getResource("test_install_ruby." + ShellToken.SH.to(OsFamily.UNIX)), + Charsets.UTF_8) + + "installChefGems || return 1\nmkdir -p /var/chef\nmkdir -p /var/chef/roles\n" + + "cat > /var/chef/roles/foo.json <<-'END_OF_JCLOUDS_FILE'\n" + + "\t{\"name\": \"foo\",\"description\":\"\",\"default_attributes\":{},\"override_attributes\":{}," + + "\"json_class\":\"Chef::Role\",\"chef_type\":\"role\",\"run_list\":[\"recipe[apache2]\"]}" + + "\nEND_OF_JCLOUDS_FILE\nchef-solo -N `hostname` -r /tmp/cookbooks -o role[foo]\n"); + } + + public void testChefSoloWithRoleDefinitionAndRoleAndRecipe() throws IOException { + Role role = Role.builder().name("foo").installRecipe("apache2").build(); + String script = ChefSolo.builder().cookbooksArchiveLocation("/tmp/cookbooks").defineRole(role).installRole("foo") + .installRecipe("git").build().render(OsFamily.UNIX); + assertEquals( + script, + Resources.toString(Resources.getResource("test_install_ruby." + ShellToken.SH.to(OsFamily.UNIX)), + Charsets.UTF_8) + + "installChefGems || return 1\nmkdir -p /var/chef\nmkdir -p /var/chef/roles\n" + + "cat > /var/chef/roles/foo.json <<-'END_OF_JCLOUDS_FILE'\n" + + "\t{\"name\": \"foo\",\"description\":\"\",\"default_attributes\":{},\"override_attributes\":{}," + + "\"json_class\":\"Chef::Role\",\"chef_type\":\"role\",\"run_list\":[\"recipe[apache2]\"]}" + + "\nEND_OF_JCLOUDS_FILE\nchef-solo -N `hostname` -r /tmp/cookbooks -o role[foo],recipe[git]\n"); + } }