From fb70b3fd229f25a14d3477b9bde07424ee0995be Mon Sep 17 00:00:00 2001 From: Ignasi Barrera Date: Wed, 6 Mar 2013 18:03:22 +0100 Subject: [PATCH] Improved RubyGems installation. Enable users to configure the version of RubyGems that will be installed and also if the system and existing gems have to be upgraded after the installation. Also modifies the InstallChefGems statement to allow users to specify the desired version of the Chef gem to install. --- .../FunctionNotFoundException.java | 2 + .../statements/chef/ChefSolo.java | 19 ++- .../statements/chef/InstallChefGems.java | 50 +++++- .../statements/ruby/InstallRuby.java | 26 +-- .../statements/ruby/InstallRubyGems.java | 144 +++++++++++++++++ .../scriptbuilder/ScriptBuilderTest.java | 1 - .../scriptbuilder/domain/SwitchArgTest.java | 2 - .../statements/chef/ChefSoloTest.java | 16 +- .../statements/chef/InstallChefGemsTest.java | 23 +-- .../statements/ruby/InstallRubyGemsTest.java | 112 +++++++++++++ .../test_install_chef_gems_scriptbuilder.sh | 100 ------------ .../src/test/resources/test_install_ruby.sh | 14 -- .../test_install_ruby_scriptbuilder.sh | 15 -- .../test/resources/test_install_rubygems.sh | 14 ++ .../test_install_rubygems_scriptbuilder.sh | 151 ++++++++++++++++++ 15 files changed, 506 insertions(+), 183 deletions(-) create mode 100644 scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ruby/InstallRubyGems.java create mode 100644 scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ruby/InstallRubyGemsTest.java create mode 100644 scriptbuilder/src/test/resources/test_install_rubygems.sh create mode 100644 scriptbuilder/src/test/resources/test_install_rubygems_scriptbuilder.sh diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/functionloader/FunctionNotFoundException.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/functionloader/FunctionNotFoundException.java index ba0650997a..8eac699de3 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/functionloader/FunctionNotFoundException.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/functionloader/FunctionNotFoundException.java @@ -22,6 +22,8 @@ import org.jclouds.scriptbuilder.domain.OsFamily; public class FunctionNotFoundException extends RuntimeException { + private static final long serialVersionUID = 1L; + public FunctionNotFoundException(String functionName, OsFamily family) { super("function: " + functionName + " not found for family: " + family); } 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 9027869907..bcdc270bd8 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 @@ -73,6 +73,7 @@ public class ChefSolo implements Statement { private List roles = Lists.newArrayList(); private List databags = Lists.newArrayList(); private RunList runlist; + private String chefVersion; /** * Directory where Chef Solo will store files. @@ -232,13 +233,22 @@ public class ChefSolo implements Statement { return this; } + /** + * The version of the Chef gem to install. + */ + public Builder chefVersion(String chefVersion) { + this.chefVersion = checkNotNull(chefVersion, "chefVersion"); + return this; + } + public ChefSolo build() { return new ChefSolo(Optional.of(fileCachePath), Optional.fromNullable(rolePath), Optional.fromNullable(databagPath), Optional.of(cookbookPath.build()), Optional.fromNullable(cookbooksArchiveLocation), Optional.fromNullable(jsonAttributes), Optional.fromNullable(group), Optional.fromNullable(interval), Optional.fromNullable(logLevel), Optional.fromNullable(logFile), Optional.fromNullable(nodeName), Optional.fromNullable(splay), - Optional.fromNullable(user), Optional.of(roles), Optional.of(databags), Optional.fromNullable(runlist)); + Optional.fromNullable(user), Optional.of(roles), Optional.of(databags), Optional.fromNullable(runlist), + Optional.fromNullable(chefVersion)); } } @@ -259,13 +269,14 @@ public class ChefSolo implements Statement { private Optional> roles; private Optional> databags; private RunList runlist; - private final InstallChefGems installChefGems = new InstallChefGems(); + private final InstallChefGems installChefGems; public ChefSolo(Optional fileCachePath, Optional rolePath, Optional databagPath, Optional> cookbookPath, Optional cookbooksArchiveLocation, Optional jsonAttributes, Optional group, Optional interval, Optional logLevel, Optional logFile, Optional nodeName, Optional splay, - Optional user, Optional> roles, Optional> databags, Optional runlist) { + Optional user, Optional> roles, Optional> databags, + Optional runlist, Optional chefVersion) { this.fileCachePath = checkNotNull(fileCachePath, "fileCachePath must be set").or(DEFAULT_SOLO_PATH); this.rolePath = checkNotNull(rolePath, "rolePath must be set").or(this.fileCachePath + "/roles"); this.databagPath = checkNotNull(databagPath, "databagPath must be set").or(this.fileCachePath + "/data_bags"); @@ -281,11 +292,13 @@ public class ChefSolo implements Statement { this.roles = checkNotNull(roles, "roles must be set"); this.databags = checkNotNull(databags, "databags must be set"); this.runlist = checkNotNull(runlist, "runlist must be set").or(RunList.builder().build()); + this.user = checkNotNull(user, "chefVersion must be set"); if (!checkNotNull(cookbookPath, "cookbookPath must be set").isPresent() || cookbookPath.get().isEmpty()) { this.cookbookPath = ImmutableList. of(this.fileCachePath + "/cookbooks"); } else { this.cookbookPath = ImmutableList. copyOf(cookbookPath.get()); } + this.installChefGems = InstallChefGems.builder().version(chefVersion.orNull()).build(); } @Override diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/chef/InstallChefGems.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/chef/InstallChefGems.java index 9128588999..5d259572d5 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/chef/InstallChefGems.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/chef/InstallChefGems.java @@ -20,21 +20,46 @@ package org.jclouds.scriptbuilder.statements.chef; import static org.jclouds.scriptbuilder.domain.Statements.exec; +import org.jclouds.javax.annotation.Nullable; import org.jclouds.scriptbuilder.domain.OsFamily; -import org.jclouds.scriptbuilder.domain.StatementList; -import org.jclouds.scriptbuilder.statements.ruby.InstallRuby; +import org.jclouds.scriptbuilder.domain.Statement; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableSet; /** * Installs Chef gems onto a host. * * @author Ignasi Barrera */ -public class InstallChefGems extends StatementList { +public class InstallChefGems implements Statement { - public InstallChefGems() { - // Chef versions prior to 10.16.4 install an incompatible moneta gem. - // See: http://tickets.opscode.com/browse/CHEF-3721 - super(new InstallRuby(), exec("gem install chef --no-rdoc --no-ri")); + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private Optional version = Optional.absent(); + + /** + * The version of the Chef gem to install. + *

+ * Can be something like '>= 0.10.8'. + */ + public Builder version(@Nullable String version) { + this.version = Optional.fromNullable(version); + return this; + } + + public InstallChefGems build() { + return new InstallChefGems(version); + } + } + + private Optional version; + + public InstallChefGems(Optional version) { + this.version = version; } @Override @@ -42,7 +67,16 @@ public class InstallChefGems extends StatementList { if (family == OsFamily.WINDOWS) { throw new UnsupportedOperationException("windows not yet implemented"); } - return super.render(family); + + Statement statement = version.isPresent() ? exec(String.format("gem install chef -v '%s' --no-rdoc --no-ri", + version.get())) : exec("gem install chef --no-rdoc --no-ri"); + + return statement.render(family); + } + + @Override + public Iterable functionDependencies(OsFamily family) { + return ImmutableSet. of(); } } diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ruby/InstallRuby.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ruby/InstallRuby.java index 833882baca..896512601f 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ruby/InstallRuby.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ruby/InstallRuby.java @@ -19,41 +19,19 @@ package org.jclouds.scriptbuilder.statements.ruby; import static org.jclouds.scriptbuilder.domain.Statements.call; -import static org.jclouds.scriptbuilder.domain.Statements.exec; -import static org.jclouds.scriptbuilder.domain.Statements.extractTargzAndFlattenIntoDirectory; - -import java.net.URI; import org.jclouds.scriptbuilder.domain.OsFamily; -import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.StatementList; /** - * Installs Ruby and Rubygems gems onto a host. + * Installs Ruby onto a host. * * @author Ignasi Barrera */ public class InstallRuby extends StatementList { - private static final URI RUBYGEMS_URI = URI.create("http://production.cf.rubygems.org/rubygems/rubygems-1.8.10.tgz"); - - public static Statement installRubyGems() { - return new StatementList(// - exec("if ! hash gem 2>/dev/null; then"), // - exec("("), // - extractTargzAndFlattenIntoDirectory(RUBYGEMS_URI, "/tmp/rubygems"), // - exec("{cd} /tmp/rubygems"), // - exec("ruby setup.rb --no-format-executable"), // - exec("{rm} -fr /tmp/rubygems"), // - exec(")"), // - exec("fi"), // - // Make sure RubyGems is up to date - exec("gem update --system"), // - exec("gem update --no-rdoc --no-ri")); - } - public InstallRuby() { - super(call("setupPublicCurl"), call("installRuby"), installRubyGems()); + super(call("setupPublicCurl"), call("installRuby")); } @Override diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ruby/InstallRubyGems.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ruby/InstallRubyGems.java new file mode 100644 index 0000000000..1b4e14a62f --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ruby/InstallRubyGems.java @@ -0,0 +1,144 @@ +/** + * 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.statements.ruby; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.scriptbuilder.domain.Statements.exec; +import static org.jclouds.scriptbuilder.domain.Statements.extractTargzAndFlattenIntoDirectory; + +import java.net.URI; + +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.scriptbuilder.domain.OsFamily; +import org.jclouds.scriptbuilder.domain.Statement; +import org.jclouds.scriptbuilder.domain.StatementList; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; + +/** + * Installs RubyGems onto a host. + * + * @author Ignasi Barrera + */ +public class InstallRubyGems implements Statement { + + public static final String DEFAULT_RUBYGEMS_VERSION = "1.8.10"; + private static final String RUBYGEMS_URI_TEMPLATE = "http://production.cf.rubygems.org/rubygems/rubygems-%s.tgz"; + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private Optional version = Optional.absent(); + private boolean updateSystem = false; + private Optional updateSystemVersion = Optional.absent(); + private boolean updateExistingGems = false; + + /** + * The version of RubyGems to install. + */ + public Builder version(@Nullable String version) { + this.version = Optional.fromNullable(version); + return this; + } + + /** + * Update the gem system after installing RubyGems. + */ + public Builder updateSystem(boolean updateSystem) { + this.updateSystem = updateSystem; + this.updateSystemVersion = Optional.absent(); + return this; + } + + /** + * Update the gem system after installing RubyGems, forcing the update to + * a concrete version. + */ + public Builder updateSystem(boolean updateSystem, @Nullable String updateSystemVersion) { + this.updateSystem = updateSystem; + this.updateSystemVersion = Optional.fromNullable(updateSystemVersion); + return this; + } + + /** + * Update the existing gems after installing RubyGems. + */ + public Builder updateExistingGems(boolean updateExistingGems) { + this.updateExistingGems = updateExistingGems; + return this; + } + + public InstallRubyGems build() { + return new InstallRubyGems(version, updateSystem, updateSystemVersion, updateExistingGems); + } + + } + + private Optional version; + private boolean updateSystem; + private Optional updateSystemVersion; + private boolean updateExistingGems; + + public InstallRubyGems(Optional version, boolean updateSystem, Optional updateSystemVersion, + boolean updateExistingGems) { + this.version = checkNotNull(version, "version must be set"); + this.updateSystem = updateSystem; + this.updateSystemVersion = checkNotNull(updateSystemVersion, "updateSystemVersion must be set"); + this.updateExistingGems = updateExistingGems; + } + + @Override + public String render(OsFamily family) { + if (family == OsFamily.WINDOWS) { + throw new UnsupportedOperationException("windows not yet implemented"); + } + + URI rubygemsUri = URI.create(String.format(RUBYGEMS_URI_TEMPLATE, version.or(DEFAULT_RUBYGEMS_VERSION))); + + ImmutableList.Builder statements = ImmutableList.builder(); + statements.add(exec("if ! hash gem 2>/dev/null; then")); + statements.add(exec("(")); + statements.add(extractTargzAndFlattenIntoDirectory(rubygemsUri, "/tmp/rubygems")); + statements.add(exec("{cd} /tmp/rubygems")); + statements.add(exec("ruby setup.rb --no-format-executable")); + statements.add(exec("{rm} -fr /tmp/rubygems")); + statements.add(exec(")")); + statements.add(exec("fi")); + + if (updateSystem) { + statements.add(updateSystemVersion.isPresent() ? exec("gem update --system " + updateSystemVersion.get()) + : exec("gem update --system")); + } + if (updateExistingGems) { + statements.add(exec("gem update --no-rdoc --no-ri")); + } + + return new StatementList(statements.build()).render(family); + } + + @Override + public Iterable functionDependencies(OsFamily family) { + return ImmutableSet. of(); + } + +} diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/ScriptBuilderTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/ScriptBuilderTest.java index f45ae0fc45..c550f49535 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/ScriptBuilderTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/ScriptBuilderTest.java @@ -26,7 +26,6 @@ import static org.jclouds.scriptbuilder.domain.Statements.kill; import static org.jclouds.scriptbuilder.domain.Statements.newStatementList; import static org.jclouds.scriptbuilder.domain.Statements.switchArg; import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; import java.io.IOException; import java.net.MalformedURLException; diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/SwitchArgTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/SwitchArgTest.java index 7f5b6bb7c5..bd36e59bde 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/SwitchArgTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/SwitchArgTest.java @@ -23,8 +23,6 @@ import static org.jclouds.scriptbuilder.domain.Statements.interpret; import static org.jclouds.scriptbuilder.domain.Statements.newStatementList; import static org.testng.Assert.assertEquals; -import java.util.Collections; - import org.testng.annotations.Test; import com.google.common.collect.ImmutableList; 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 8f423ba8a1..e2d36d7b67 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 @@ -26,17 +26,14 @@ import static org.testng.Assert.assertTrue; import java.io.IOException; import org.jclouds.scriptbuilder.domain.OsFamily; -import org.jclouds.scriptbuilder.domain.ShellToken; import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.chef.DataBag; import org.jclouds.scriptbuilder.domain.chef.Role; import org.jclouds.scriptbuilder.domain.chef.RunList; import org.testng.annotations.Test; -import com.google.common.base.Charsets; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; -import com.google.common.io.Resources; /** * Unit tests for the {@link ChefSoloTest} statement. @@ -380,9 +377,18 @@ public class ChefSoloTest { + "chef-solo -c /var/chef/solo.rb -j /var/chef/node.json -N `hostname` -u foo\n"); } + public void testChefSoloWithChefGemVersion() throws IOException { + String script = ChefSolo.builder().chefVersion(">= 0.10.8").build().render(OsFamily.UNIX); + assertEquals(script, installChefGems(">= 0.10.8") + createConfigFile() + createNodeFile() + + "chef-solo -c /var/chef/solo.rb -j /var/chef/node.json -N `hostname`\n"); + } + private static String installChefGems() throws IOException { - return Resources.toString(Resources.getResource("test_install_ruby." + ShellToken.SH.to(OsFamily.UNIX)), - Charsets.UTF_8) + "gem install chef --no-rdoc --no-ri\n"; + return "gem install chef --no-rdoc --no-ri\n"; + } + + private static String installChefGems(String version) throws IOException { + return "gem install chef -v '" + version + "' --no-rdoc --no-ri\n"; } private static String createConfigFile() { diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/chef/InstallChefGemsTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/chef/InstallChefGemsTest.java index 2f9ad7df74..d828c2ea10 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/chef/InstallChefGemsTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/chef/InstallChefGemsTest.java @@ -40,21 +40,22 @@ public class InstallChefGemsTest { @Test(expectedExceptions = UnsupportedOperationException.class, expectedExceptionsMessageRegExp = "windows not yet implemented") public void installChefGemsInWindows() { - new InstallChefGems().render(OsFamily.WINDOWS); + InstallChefGems.builder().build().render(OsFamily.WINDOWS); } public void installChefGemsUnix() throws IOException { - assertEquals( - new InstallChefGems().render(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"); + assertEquals(InstallChefGems.builder().build().render(OsFamily.UNIX), "gem install chef --no-rdoc --no-ri\n"); } - public void installChefGemsUnixInScriptBuilderSourcesSetupPublicCurl() throws IOException { - assertEquals( - InitScript.builder().name("install_chef_gems").run(new InstallChefGems()).build().render(OsFamily.UNIX), - Resources.toString( - Resources.getResource("test_install_chef_gems_scriptbuilder." + ShellToken.SH.to(OsFamily.UNIX)), - Charsets.UTF_8)); + public void installChefGemsUnixWithCustomVersion() throws IOException { + assertEquals(InstallChefGems.builder().version(">= 0.10.8").build().render(OsFamily.UNIX), + "gem install chef -v '>= 0.10.8' --no-rdoc --no-ri\n"); + } + + public void installChefGemsUnixInScriptBuilder() throws IOException { + assertEquals(InitScript.builder().name("install_chef_gems").run(InstallChefGems.builder().build()).build() + .render(OsFamily.UNIX), Resources.toString( + Resources.getResource("test_install_chef_gems_scriptbuilder." + ShellToken.SH.to(OsFamily.UNIX)), + Charsets.UTF_8)); } } diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ruby/InstallRubyGemsTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ruby/InstallRubyGemsTest.java new file mode 100644 index 0000000000..7c61b2f879 --- /dev/null +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ruby/InstallRubyGemsTest.java @@ -0,0 +1,112 @@ +/** + * 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.statements.ruby; + +import static org.jclouds.scriptbuilder.statements.ruby.InstallRubyGems.DEFAULT_RUBYGEMS_VERSION; +import static org.testng.Assert.assertEquals; + +import java.io.IOException; + +import org.jclouds.scriptbuilder.InitScript; +import org.jclouds.scriptbuilder.domain.OsFamily; +import org.jclouds.scriptbuilder.domain.ShellToken; +import org.testng.annotations.Test; + +import com.google.common.base.Charsets; +import com.google.common.io.Resources; + +/** + * Unit tests for the {@link InstallRubyGemsTest} statement. + * + * @author Ignasi Barrera + */ +@Test(groups = "unit", testName = "InstallRubyGemsTest") +public class InstallRubyGemsTest { + + @Test(expectedExceptions = UnsupportedOperationException.class, expectedExceptionsMessageRegExp = "windows not yet implemented") + public void installRubyGemsInWindows() { + new InstallRuby().render(OsFamily.WINDOWS); + } + + public void installRubyGemsDefaultsUnix() throws IOException { + assertEquals(InstallRubyGems.builder().build().render(OsFamily.UNIX), installRubyGems(DEFAULT_RUBYGEMS_VERSION)); + } + + public void installRubyGemsForcingVersion() throws IOException { + assertEquals(InstallRubyGems.builder().version("1.8.25").build().render(OsFamily.UNIX), installRubyGems("1.8.25")); + } + + public void installRubyGemsAndUpdateSystem() throws IOException { + assertEquals(InstallRubyGems.builder().updateSystem(true).build().render(OsFamily.UNIX), + installRubyGems(DEFAULT_RUBYGEMS_VERSION) + updateSystem(null)); + } + + public void installRubyGemsAndUpdateSystemForcingUpdateVersion() throws IOException { + assertEquals(InstallRubyGems.builder().updateSystem(true, "1.8.25").build().render(OsFamily.UNIX), + installRubyGems(DEFAULT_RUBYGEMS_VERSION) + updateSystem("1.8.25")); + } + + public void installRubyGemsAndUpdateGems() throws IOException { + assertEquals(InstallRubyGems.builder().updateExistingGems(true).build().render(OsFamily.UNIX), + installRubyGems(DEFAULT_RUBYGEMS_VERSION) + updateGems()); + } + + public void installRubyGemsUpdatingSystemAndGems() throws IOException { + assertEquals(InstallRubyGems.builder().version("1.2.3").updateSystem(true, "1.2.4").updateExistingGems(true) + .build().render(OsFamily.UNIX), installRubyGems("1.2.3") + updateSystem("1.2.4") + updateGems()); + } + + public void installRubyGemsDefaultsWithUpgrade() throws IOException { + assertEquals(InstallRubyGems.builder().updateSystem(true).updateExistingGems(true).build().render(OsFamily.UNIX), + Resources.toString(Resources.getResource("test_install_rubygems." + ShellToken.SH.to(OsFamily.UNIX)), + Charsets.UTF_8)); + } + + public void installRubyGemsUnixDefaultsInScriptBuilder() throws IOException { + assertEquals( + InitScript.builder().name("install_rubygems").run(InstallRubyGems.builder().build()).build() + .render(OsFamily.UNIX), Resources.toString( + Resources.getResource("test_install_rubygems_scriptbuilder." + ShellToken.SH.to(OsFamily.UNIX)), + Charsets.UTF_8)); + } + + private static String installRubyGems(String version) { + String script = "if ! hash gem 2>/dev/null; then\n" + + "(\n" + + "mkdir /tmp/$$\n" + + "curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET http://production.cf.rubygems.org/rubygems/rubygems-" + + version + ".tgz |(mkdir -p /tmp/$$ &&cd /tmp/$$ &&tar -xpzf -)\n" + "mkdir -p /tmp/rubygems\n" + + "mv /tmp/$$/*/* /tmp/rubygems\n" + "rm -rf /tmp/$$\n" + "cd /tmp/rubygems\n" + + "ruby setup.rb --no-format-executable\n" // + + "rm -fr /tmp/rubygems\n" + // + ")\n" + // + "fi\n"; + + return script; + } + + private static String updateSystem(String version) { + return version == null ? "gem update --system\n" : "gem update --system " + version + "\n"; + } + + private static String updateGems() { + return "gem update --no-rdoc --no-ri\n"; + } + +} diff --git a/scriptbuilder/src/test/resources/test_install_chef_gems_scriptbuilder.sh b/scriptbuilder/src/test/resources/test_install_chef_gems_scriptbuilder.sh index b96fa7695b..d08f1cd055 100644 --- a/scriptbuilder/src/test/resources/test_install_chef_gems_scriptbuilder.sh +++ b/scriptbuilder/src/test/resources/test_install_chef_gems_scriptbuilder.sh @@ -77,89 +77,6 @@ END_OF_JCLOUDS_SCRIPT export INSTANCE_NAME='$INSTANCE_NAME' export INSTANCE_HOME='$INSTANCE_HOME' export LOG_DIR='$LOG_DIR' -END_OF_JCLOUDS_SCRIPT - cat >> $INSTANCE_HOME/install_chef_gems.sh <<-'END_OF_JCLOUDS_SCRIPT' - function abort { - echo "aborting: $@" 1>&2 - exit 1 -} -alias apt-get-update="apt-get update -qq" -alias apt-get-install="apt-get install -f -y -qq --force-yes" -alias yum-install="yum --quiet --nogpgcheck -y install" - -function ensure_cmd_or_install_package_apt(){ - local cmd=$1 - shift - local pkg=$* - - hash $cmd 2>/dev/null || ( apt-get-update && apt-get-install $pkg ) -} - -function ensure_cmd_or_install_package_yum(){ - local cmd=$1 - shift - local pkg=$* - hash $cmd 2>/dev/null || yum-install $pkg -} - -function ensure_netutils_apt() { - ensure_cmd_or_install_package_apt nslookup dnsutils - ensure_cmd_or_install_package_apt curl curl -} - -function ensure_netutils_yum() { - ensure_cmd_or_install_package_yum nslookup bind-utils - ensure_cmd_or_install_package_yum curl curl -} - -# most network services require that the hostname is in -# the /etc/hosts file, or they won't operate -function ensure_hostname_in_hosts() { - [ -n "$SSH_CONNECTION" ] && { - local ipaddr=`echo $SSH_CONNECTION | awk '{print $3}'` - } || { - local ipaddr=`hostname -i` - } - # NOTE: we blindly trust existing hostname settings in /etc/hosts - egrep -q `hostname` /etc/hosts || echo "$ipaddr `hostname`" >> /etc/hosts -} - -# download locations for many services are at public dns -function ensure_can_resolve_public_dns() { - nslookup yahoo.com | grep yahoo.com > /dev/null || echo nameserver 208.67.222.222 >> /etc/resolv.conf -} - -function setupPublicCurl() { - ensure_hostname_in_hosts - if which dpkg &> /dev/null; then - ensure_netutils_apt - elif which rpm &> /dev/null; then - ensure_netutils_yum - else - abort "we only support apt-get and yum right now... please contribute!" - return 1 - fi - ensure_can_resolve_public_dns - return 0 -} -function installRuby() { - if ! hash ruby 2>/dev/null; then - if which dpkg &> /dev/null; then - apt-get-update - apt-get install -y ruby ruby-dev build-essential - elif which rpm &> /dev/null; then - # Disable chef from the base repo (http://tickets.opscode.com/browse/CHEF-2906) - sed -i "s/\[base\]/\0\n\exclude=ruby*/g" /etc/yum.repos.d/CentOS-Base.repo - # Make sure to install an appropriate ruby version - yum erase -y ruby ruby-libs - rpm -Uvh http://rbel.co/rbel5 - yum install -y ruby ruby-devel make gcc gcc-c++ automake autoconf - else - abort "we only support apt-get and yum right now... please contribute" - fi - fi -} - END_OF_JCLOUDS_SCRIPT # add desired commands from the user @@ -167,23 +84,6 @@ END_OF_JCLOUDS_SCRIPT cd $INSTANCE_HOME rm -f $INSTANCE_HOME/rc trap 'echo $?>$INSTANCE_HOME/rc' 0 1 2 3 15 - setupPublicCurl || exit 1 - installRuby || exit 1 - if ! hash gem 2>/dev/null; then - ( - mkdir /tmp/$$ - curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET http://production.cf.rubygems.org/rubygems/rubygems-1.8.10.tgz |(mkdir -p /tmp/$$ &&cd /tmp/$$ &&tar -xpzf -) - mkdir -p /tmp/rubygems - mv /tmp/$$/*/* /tmp/rubygems - rm -rf /tmp/$$ - cd /tmp/rubygems - ruby setup.rb --no-format-executable - rm -fr /tmp/rubygems - ) - fi - gem update --system - gem update --no-rdoc --no-ri - gem install chef --no-rdoc --no-ri END_OF_JCLOUDS_SCRIPT diff --git a/scriptbuilder/src/test/resources/test_install_ruby.sh b/scriptbuilder/src/test/resources/test_install_ruby.sh index 16ecc36a9c..631cbb1bb8 100644 --- a/scriptbuilder/src/test/resources/test_install_ruby.sh +++ b/scriptbuilder/src/test/resources/test_install_ruby.sh @@ -1,16 +1,2 @@ setupPublicCurl || return 1 installRuby || return 1 -if ! hash gem 2>/dev/null; then -( -mkdir /tmp/$$ -curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET http://production.cf.rubygems.org/rubygems/rubygems-1.8.10.tgz |(mkdir -p /tmp/$$ &&cd /tmp/$$ &&tar -xpzf -) -mkdir -p /tmp/rubygems -mv /tmp/$$/*/* /tmp/rubygems -rm -rf /tmp/$$ -cd /tmp/rubygems -ruby setup.rb --no-format-executable -rm -fr /tmp/rubygems -) -fi -gem update --system -gem update --no-rdoc --no-ri diff --git a/scriptbuilder/src/test/resources/test_install_ruby_scriptbuilder.sh b/scriptbuilder/src/test/resources/test_install_ruby_scriptbuilder.sh index 3eb8fc45aa..0ae5aaba52 100644 --- a/scriptbuilder/src/test/resources/test_install_ruby_scriptbuilder.sh +++ b/scriptbuilder/src/test/resources/test_install_ruby_scriptbuilder.sh @@ -171,21 +171,6 @@ END_OF_JCLOUDS_SCRIPT installRuby || exit 1 - if ! hash gem 2>/dev/null; then - ( - mkdir /tmp/$$ - curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET http://production.cf.rubygems.org/rubygems/rubygems-1.8.10.tgz |(mkdir -p /tmp/$$ &&cd /tmp/$$ &&tar -xpzf -) - mkdir -p /tmp/rubygems - mv /tmp/$$/*/* /tmp/rubygems - rm -rf /tmp/$$ - cd /tmp/rubygems - ruby setup.rb --no-format-executable - rm -fr /tmp/rubygems - ) - fi - gem update --system - gem update --no-rdoc --no-ri - END_OF_JCLOUDS_SCRIPT # add runscript footer diff --git a/scriptbuilder/src/test/resources/test_install_rubygems.sh b/scriptbuilder/src/test/resources/test_install_rubygems.sh new file mode 100644 index 0000000000..c9363d24cc --- /dev/null +++ b/scriptbuilder/src/test/resources/test_install_rubygems.sh @@ -0,0 +1,14 @@ +if ! hash gem 2>/dev/null; then +( +mkdir /tmp/$$ +curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET http://production.cf.rubygems.org/rubygems/rubygems-1.8.10.tgz |(mkdir -p /tmp/$$ &&cd /tmp/$$ &&tar -xpzf -) +mkdir -p /tmp/rubygems +mv /tmp/$$/*/* /tmp/rubygems +rm -rf /tmp/$$ +cd /tmp/rubygems +ruby setup.rb --no-format-executable +rm -fr /tmp/rubygems +) +fi +gem update --system +gem update --no-rdoc --no-ri diff --git a/scriptbuilder/src/test/resources/test_install_rubygems_scriptbuilder.sh b/scriptbuilder/src/test/resources/test_install_rubygems_scriptbuilder.sh new file mode 100644 index 0000000000..1c4bb5f8c5 --- /dev/null +++ b/scriptbuilder/src/test/resources/test_install_rubygems_scriptbuilder.sh @@ -0,0 +1,151 @@ +#!/bin/bash +set +u +shopt -s xpg_echo +shopt -s expand_aliases +unset PATH JAVA_HOME LD_LIBRARY_PATH +function abort { + echo "aborting: $@" 1>&2 + exit 1 +} +function default { + export INSTANCE_NAME="install_rubygems" +export INSTANCE_HOME="/tmp/$INSTANCE_NAME" +export LOG_DIR="$INSTANCE_HOME" + return $? +} +function install_rubygems { + return $? +} +function findPid { + unset FOUND_PID; + [ $# -eq 1 ] || { + abort "findPid requires a parameter of pattern to match" + return 1 + } + local PATTERN="$1"; shift + local _FOUND=`ps auxwww|grep "$PATTERN"|grep -v " $0"|grep -v grep|grep -v $$|awk '{print $2}'` + [ -n "$_FOUND" ] && { + export FOUND_PID=$_FOUND + return 0 + } || { + return 1 + } +} +function forget { + unset FOUND_PID; + [ $# -eq 3 ] || { + abort "forget requires parameters INSTANCE_NAME SCRIPT LOG_DIR" + return 1 + } + local INSTANCE_NAME="$1"; shift + local SCRIPT="$1"; shift + local LOG_DIR="$1"; shift + mkdir -p $LOG_DIR + findPid $INSTANCE_NAME + [ -n "$FOUND_PID" -a -f $LOG_DIR/stdout.log ] && { + echo $INSTANCE_NAME already running pid $FOUND_PID + return 1; + } || { + nohup $SCRIPT >$LOG_DIR/stdout.log 2>$LOG_DIR/stderr.log & + RETURN=$? + # this is generally followed by findPid, so we shouldn't exit + # immediately as the proc may not have registered in ps, yet + test $RETURN && sleep 1 + return $RETURN; + } +} +export PATH=/usr/ucb/bin:/bin:/sbin:/usr/bin:/usr/sbin +case $1 in +init) + default || exit 1 + install_rubygems || exit 1 + mkdir -p $INSTANCE_HOME + + # create runscript header + cat > $INSTANCE_HOME/install_rubygems.sh <<-'END_OF_JCLOUDS_SCRIPT' + #!/bin/bash + set +u + shopt -s xpg_echo + shopt -s expand_aliases + + PROMPT_COMMAND='echo -ne \"\033]0;install_rubygems\007\"' + export PATH=/usr/ucb/bin:/bin:/sbin:/usr/bin:/usr/sbin + + export INSTANCE_NAME='install_rubygems' +END_OF_JCLOUDS_SCRIPT + cat >> $INSTANCE_HOME/install_rubygems.sh <<-END_OF_JCLOUDS_SCRIPT + export INSTANCE_NAME='$INSTANCE_NAME' + export INSTANCE_HOME='$INSTANCE_HOME' + export LOG_DIR='$LOG_DIR' +END_OF_JCLOUDS_SCRIPT + + # add desired commands from the user + cat >> $INSTANCE_HOME/install_rubygems.sh <<-'END_OF_JCLOUDS_SCRIPT' + cd $INSTANCE_HOME + rm -f $INSTANCE_HOME/rc + trap 'echo $?>$INSTANCE_HOME/rc' 0 1 2 3 15 + if ! hash gem 2>/dev/null; then + ( + mkdir /tmp/$$ + curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET http://production.cf.rubygems.org/rubygems/rubygems-1.8.10.tgz |(mkdir -p /tmp/$$ &&cd /tmp/$$ &&tar -xpzf -) + mkdir -p /tmp/rubygems + mv /tmp/$$/*/* /tmp/rubygems + rm -rf /tmp/$$ + cd /tmp/rubygems + ruby setup.rb --no-format-executable + rm -fr /tmp/rubygems + ) + fi + +END_OF_JCLOUDS_SCRIPT + + # add runscript footer + cat >> $INSTANCE_HOME/install_rubygems.sh <<-'END_OF_JCLOUDS_SCRIPT' + exit $? + +END_OF_JCLOUDS_SCRIPT + + chmod u+x $INSTANCE_HOME/install_rubygems.sh + ;; +status) + default || exit 1 + findPid $INSTANCE_NAME || exit 1 + echo $FOUND_PID + ;; +stop) + default || exit 1 + findPid $INSTANCE_NAME || exit 1 + [ -n "$FOUND_PID" ] && { + echo stopping $FOUND_PID + kill -9 $FOUND_PID + } + ;; +start) + default || exit 1 + forget $INSTANCE_NAME $INSTANCE_HOME/$INSTANCE_NAME.sh $LOG_DIR || exit 1 + ;; +stdout) + default || exit 1 + cat $LOG_DIR/stdout.log + ;; +stderr) + default || exit 1 + cat $LOG_DIR/stderr.log + ;; +exitstatus) + default || exit 1 + [ -f $LOG_DIR/rc ] && cat $LOG_DIR/rc;; +tail) + default || exit 1 + tail $LOG_DIR/stdout.log + ;; +tailerr) + default || exit 1 + tail $LOG_DIR/stderr.log + ;; +run) + default || exit 1 + $INSTANCE_HOME/$INSTANCE_NAME.sh + ;; +esac +exit $?