From 61258a64f6d9d04a2b533895d32cfbb32a84060c Mon Sep 17 00:00:00 2001 From: Ignasi Barrera Date: Tue, 17 Sep 2013 14:43:16 +0200 Subject: [PATCH] JCLOUDS-286: Use by default the Omnibus installer --- .../org/jclouds/chef/ChefApiMetadata.java | 4 +- .../chef/config/ChefBootstrapModule.java | 28 +++++- .../jclouds/chef/config/ChefProperties.java | 15 ++- .../org/jclouds/chef/config/InstallChef.java | 39 ++++++++ .../chef/functions/GroupToBootScript.java | 10 +- .../org/jclouds/chef/ChefApiExpectTest.java | 2 +- .../chef/functions/GroupToBootScriptTest.java | 93 ++++++++++++++++--- 7 files changed, 167 insertions(+), 24 deletions(-) create mode 100644 apis/chef/src/main/java/org/jclouds/chef/config/InstallChef.java diff --git a/apis/chef/src/main/java/org/jclouds/chef/ChefApiMetadata.java b/apis/chef/src/main/java/org/jclouds/chef/ChefApiMetadata.java index e28b481ec7..951d2d2e1c 100644 --- a/apis/chef/src/main/java/org/jclouds/chef/ChefApiMetadata.java +++ b/apis/chef/src/main/java/org/jclouds/chef/ChefApiMetadata.java @@ -23,6 +23,7 @@ import static org.jclouds.Constants.PROPERTY_TIMEOUTS_PREFIX; import static org.jclouds.chef.config.ChefProperties.CHEF_BOOTSTRAP_DATABAG; import static org.jclouds.chef.config.ChefProperties.CHEF_UPDATE_GEMS; import static org.jclouds.chef.config.ChefProperties.CHEF_UPDATE_GEM_SYSTEM; +import static org.jclouds.chef.config.ChefProperties.CHEF_USE_OMNIBUS; import java.net.URI; import java.util.Properties; @@ -44,7 +45,7 @@ import com.google.inject.Module; * @author Ignasi Barrera */ public class ChefApiMetadata extends BaseHttpApiMetadata { - + /** * The default Chef Server API version to use. */ @@ -78,6 +79,7 @@ public class ChefApiMetadata extends BaseHttpApiMetadata { properties.setProperty(CHEF_BOOTSTRAP_DATABAG, "bootstrap"); properties.setProperty(CHEF_UPDATE_GEM_SYSTEM, "false"); properties.setProperty(CHEF_UPDATE_GEMS, "false"); + properties.setProperty(CHEF_USE_OMNIBUS, "true"); return properties; } diff --git a/apis/chef/src/main/java/org/jclouds/chef/config/ChefBootstrapModule.java b/apis/chef/src/main/java/org/jclouds/chef/config/ChefBootstrapModule.java index 3d9d04fb74..65d1422f24 100644 --- a/apis/chef/src/main/java/org/jclouds/chef/config/ChefBootstrapModule.java +++ b/apis/chef/src/main/java/org/jclouds/chef/config/ChefBootstrapModule.java @@ -19,6 +19,7 @@ package org.jclouds.chef.config; import static org.jclouds.chef.config.ChefProperties.CHEF_GEM_SYSTEM_VERSION; import static org.jclouds.chef.config.ChefProperties.CHEF_UPDATE_GEMS; import static org.jclouds.chef.config.ChefProperties.CHEF_UPDATE_GEM_SYSTEM; +import static org.jclouds.chef.config.ChefProperties.CHEF_USE_OMNIBUS; import static org.jclouds.chef.config.ChefProperties.CHEF_VERSION; import javax.inject.Named; @@ -27,6 +28,7 @@ import javax.inject.Singleton; import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.StatementList; import org.jclouds.scriptbuilder.statements.chef.InstallChefGems; +import org.jclouds.scriptbuilder.statements.chef.InstallChefUsingOmnibus; import org.jclouds.scriptbuilder.statements.ruby.InstallRuby; import org.jclouds.scriptbuilder.statements.ruby.InstallRubyGems; @@ -45,8 +47,7 @@ public class ChefBootstrapModule extends AbstractModule { @Provides @Named("installChefGems") @Singleton - Statement installChef(BootstrapProperties bootstrapProperties) { - + Statement installChefGems(BootstrapProperties bootstrapProperties) { InstallRubyGems installRubyGems = InstallRubyGems.builder() .version(bootstrapProperties.gemSystemVersion().orNull()) .updateSystem(bootstrapProperties.updateGemSystem(), bootstrapProperties.gemSystemVersion().orNull()) @@ -58,6 +59,21 @@ public class ChefBootstrapModule extends AbstractModule { return new StatementList(InstallRuby.builder().build(), installRubyGems, installChef); } + @Provides + @Named("installChefOmnibus") + @Singleton + Statement installChefUsingOmnibus() { + return new InstallChefUsingOmnibus(); + } + + @Provides + @InstallChef + @Singleton + Statement installChef(BootstrapProperties bootstrapProperties, @Named("installChefGems") Statement installChefGems, + @Named("installChefOmnibus") Statement installChefOmnibus) { + return bootstrapProperties.useOmnibus() ? installChefOmnibus : installChefGems; + } + @Singleton private static class BootstrapProperties { @Named(CHEF_VERSION) @@ -76,6 +92,10 @@ public class ChefBootstrapModule extends AbstractModule { @Inject private String updateGemsProperty; + @Named(CHEF_USE_OMNIBUS) + @Inject + private String useOmnibus; + public Optional chefVersion() { return Optional.fromNullable(chefVersionProperty); } @@ -91,6 +111,10 @@ public class ChefBootstrapModule extends AbstractModule { public boolean updateGems() { return Boolean.parseBoolean(updateGemsProperty); } + + public boolean useOmnibus() { + return Boolean.parseBoolean(useOmnibus); + } } @Override diff --git a/apis/chef/src/main/java/org/jclouds/chef/config/ChefProperties.java b/apis/chef/src/main/java/org/jclouds/chef/config/ChefProperties.java index 75b3aa8855..a6cf5f2ea9 100644 --- a/apis/chef/src/main/java/org/jclouds/chef/config/ChefProperties.java +++ b/apis/chef/src/main/java/org/jclouds/chef/config/ChefProperties.java @@ -31,7 +31,7 @@ public interface ChefProperties { public static final String CHEF_LOGGER = "jclouds.chef"; /** - * Ddatabag that holds chef bootstrap hints, should be a json ball in the + * Databag that holds chef bootstrap hints, should be a json ball in the * following format: *

* {"tag":{"run_list":["recipe[apache2]"]}} @@ -91,7 +91,7 @@ public interface ChefProperties { /** * Boolean property. Default (false). *

- * When bootstrapping a node, updates teh existing gems before installing + * When bootstrapping a node, updates the existing gems before installing * Chef. *

* This property must be set prior to running the @@ -99,4 +99,15 @@ public interface ChefProperties { */ public static final String CHEF_UPDATE_GEMS = "chef.update-gems"; + /** + * Boolean property. Default (true). + *

+ * When bootstrapping a node, install the Chef client using the Omnibus + * installer. + *

+ * This property must be set prior to running the + * {@link ChefService#createBootstrapScriptForGroup(String)} method. + */ + public static final String CHEF_USE_OMNIBUS = "chef.use-omnibus"; + } diff --git a/apis/chef/src/main/java/org/jclouds/chef/config/InstallChef.java b/apis/chef/src/main/java/org/jclouds/chef/config/InstallChef.java new file mode 100644 index 0000000000..909b99a88d --- /dev/null +++ b/apis/chef/src/main/java/org/jclouds/chef/config/InstallChef.java @@ -0,0 +1,39 @@ +/* + * 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.chef.config; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +/** + * Used to configure the Chef install script. + * + * @author Ignasi Barrera + */ +@Target({ METHOD, PARAMETER, FIELD }) +@Retention(RUNTIME) +@Qualifier +public @interface InstallChef { + +} 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 9154471a72..707119bbb1 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 @@ -34,11 +34,13 @@ import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; +import org.jclouds.chef.config.InstallChef; import org.jclouds.chef.config.Validator; import org.jclouds.crypto.Pems; import org.jclouds.domain.JsonBall; import org.jclouds.json.Json; import org.jclouds.location.Provider; +import org.jclouds.scriptbuilder.ExitInsteadOfReturn; import org.jclouds.scriptbuilder.domain.Statement; import com.google.common.annotations.VisibleForTesting; @@ -68,19 +70,19 @@ public class GroupToBootScript implements Function { private final Supplier endpoint; private final Json json; private final CacheLoader bootstrapConfigForGroup; - private final Statement installChefGems; + private final Statement installChef; private final Optional validatorName; private final Optional validatorCredential; @Inject public GroupToBootScript(@Provider Supplier endpoint, Json json, CacheLoader bootstrapConfigForGroup, - @Named("installChefGems") Statement installChefGems, @Validator Optional validatorName, + @InstallChef Statement installChef, @Validator Optional validatorName, @Validator Optional validatorCredential) { this.endpoint = checkNotNull(endpoint, "endpoint"); this.json = checkNotNull(json, "json"); this.bootstrapConfigForGroup = checkNotNull(bootstrapConfigForGroup, "bootstrapConfigForGroup"); - this.installChefGems = checkNotNull(installChefGems, "installChefGems"); + this.installChef = checkNotNull(installChef, "installChef"); this.validatorName = checkNotNull(validatorName, "validatorName"); this.validatorCredential = checkNotNull(validatorCredential, validatorCredential); } @@ -124,7 +126,7 @@ public class GroupToBootScript implements Function { String strOptions = Joiner.on(' ').withKeyValueSeparator(" ").join(options.build()); Statement runChef = exec("chef-client " + strOptions); - return newStatementList(installChefGems, createChefConfigDir, createClientRb, createValidationPem, + return newStatementList(new ExitInsteadOfReturn(installChef), createChefConfigDir, createClientRb, createValidationPem, createFirstBoot, runChef); } diff --git a/apis/chef/src/test/java/org/jclouds/chef/ChefApiExpectTest.java b/apis/chef/src/test/java/org/jclouds/chef/ChefApiExpectTest.java index 634c5da009..3a7bae4ff5 100644 --- a/apis/chef/src/test/java/org/jclouds/chef/ChefApiExpectTest.java +++ b/apis/chef/src/test/java/org/jclouds/chef/ChefApiExpectTest.java @@ -45,7 +45,7 @@ public class ChefApiExpectTest extends BaseChefApiExpectTest { provider = "chef"; } - private HttpRequest.Builder getHttpRequestBuilder(String method, String endPoint) { + private HttpRequest.Builder getHttpRequestBuilder(String method, String endPoint) { return HttpRequest.builder() // .method(method) // .endpoint("http://localhost:4000" + endPoint) // 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 7b51f04f09..fa9cf3f718 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 @@ -22,6 +22,7 @@ import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.verify; import static org.jclouds.chef.config.ChefProperties.CHEF_UPDATE_GEMS; import static org.jclouds.chef.config.ChefProperties.CHEF_UPDATE_GEM_SYSTEM; +import static org.jclouds.chef.config.ChefProperties.CHEF_USE_OMNIBUS; import static org.testng.Assert.assertEquals; import java.io.IOException; @@ -31,6 +32,7 @@ import java.security.PrivateKey; import org.jclouds.chef.ChefApiMetadata; import org.jclouds.chef.config.ChefBootstrapModule; import org.jclouds.chef.config.ChefParserModule; +import org.jclouds.chef.config.InstallChef; import org.jclouds.chef.domain.DatabagItem; import org.jclouds.crypto.PemsTest; import org.jclouds.domain.JsonBall; @@ -64,21 +66,34 @@ public class GroupToBootScriptTest { private Json json; private Statement installChefGems; + private Statement installChefOmnibus; private Optional validatorName; @BeforeClass public void setup() { - Injector injector = Guice.createInjector(new AbstractModule() { + Injector injectorGems = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(String.class).annotatedWith(ApiVersion.class).toInstance(ChefApiMetadata.DEFAULT_API_VERSION); bind(String.class).annotatedWith(Names.named(CHEF_UPDATE_GEM_SYSTEM)).toInstance("true"); bind(String.class).annotatedWith(Names.named(CHEF_UPDATE_GEMS)).toInstance("true"); + bind(String.class).annotatedWith(Names.named(CHEF_USE_OMNIBUS)).toInstance("false"); } }, new ChefParserModule(), new GsonModule(), new ChefBootstrapModule()); - json = injector.getInstance(Json.class); - installChefGems = injector.getInstance(Key.get(Statement.class, Names.named("installChefGems"))); + Injector injectorOmnibus = Guice.createInjector(new AbstractModule() { + @Override + protected void configure() { + bind(String.class).annotatedWith(ApiVersion.class).toInstance(ChefApiMetadata.DEFAULT_API_VERSION); + bind(String.class).annotatedWith(Names.named(CHEF_UPDATE_GEM_SYSTEM)).toInstance("true"); + bind(String.class).annotatedWith(Names.named(CHEF_UPDATE_GEMS)).toInstance("true"); + bind(String.class).annotatedWith(Names.named(CHEF_USE_OMNIBUS)).toInstance("true"); + } + }, new ChefParserModule(), new GsonModule(), new ChefBootstrapModule()); + + json = injectorGems.getInstance(Json.class); + installChefGems = injectorGems.getInstance(Key.get(Statement.class, InstallChef.class)); + installChefOmnibus = injectorOmnibus.getInstance(Key.get(Statement.class, InstallChef.class)); validatorName = Optional. of("chef-validator"); } @@ -130,13 +145,15 @@ public class GroupToBootScriptTest { 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)), + exitInsteadOfReturn( + OsFamily.UNIX, + Resources.toString(Resources.getResource("test_install_ruby." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8) - + "gem install chef --no-rdoc --no-ri\n" - + Resources.toString(Resources.getResource("bootstrap.sh"), 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.sh"), Charsets.UTF_8))); verify(validatorKey); } @@ -155,14 +172,62 @@ public class GroupToBootScriptTest { 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)), + exitInsteadOfReturn( + OsFamily.UNIX, + Resources.toString(Resources.getResource("test_install_ruby." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8) - + "gem install chef --no-rdoc --no-ri\n" + + 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); + } + + public void testOneRecipeOmnibus() 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]\"]}")))), + installChefOmnibus, validatorName, validatorCredential); + + PrivateKey validatorKey = validatorCredential.get(); + expect(validatorKey.getEncoded()).andReturn(PemsTest.PRIVATE_KEY.getBytes()); + replay(validatorKey); + + assertEquals( + fn.apply("foo").render(OsFamily.UNIX), + "setupPublicCurl || exit 1\ncurl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 " + + "-X GET https://www.opscode.com/chef/install.sh |(bash)\n" + + Resources.toString(Resources.getResource("bootstrap.sh"), Charsets.UTF_8)); + + verify(validatorKey); + } + + public void testOneRecipeAndEnvironmentOmnibus() 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]\"]}")))), installChefOmnibus, + validatorName, validatorCredential); + + PrivateKey validatorKey = validatorCredential.get(); + expect(validatorKey.getEncoded()).andReturn(PemsTest.PRIVATE_KEY.getBytes()); + replay(validatorKey); + + assertEquals( + fn.apply("foo").render(OsFamily.UNIX), + "setupPublicCurl || exit 1\ncurl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 " + + "-X GET https://www.opscode.com/chef/install.sh |(bash)\n" + Resources.toString(Resources.getResource("bootstrap-env.sh"), Charsets.UTF_8)); verify(validatorKey); } + + private static String exitInsteadOfReturn(OsFamily family, String input) { + return input.replaceAll(ShellToken.RETURN.to(family), ShellToken.EXIT.to(family)); + } + }