Refactored Ruby installation into a function

Refactored Ruby installation into a separate function, and use the
Statements method to download and extract the Rubygems tarball.
This commit is contained in:
Ignasi Barrera 2012-11-04 21:45:35 +01:00
parent 3adb6a3efb
commit 6b903df372
10 changed files with 419 additions and 47 deletions

View File

@ -22,6 +22,7 @@ import static org.jclouds.scriptbuilder.domain.Statements.call;
import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.OsFamily;
import org.jclouds.scriptbuilder.domain.StatementList; import org.jclouds.scriptbuilder.domain.StatementList;
import org.jclouds.scriptbuilder.statements.ruby.InstallRuby;
/** /**
* Installs Chef gems onto a host. * Installs Chef gems onto a host.
@ -31,7 +32,7 @@ import org.jclouds.scriptbuilder.domain.StatementList;
public class InstallChefGems extends StatementList { public class InstallChefGems extends StatementList {
public InstallChefGems() { public InstallChefGems() {
super(call("setupPublicCurl"), call("installChefGems")); super(new InstallRuby(), call("installChefGems"));
} }
@Override @Override

View File

@ -0,0 +1,62 @@
/**
* 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.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.
*
* @author Ignasi Barrera
*/
public class InstallRuby extends StatementList {
private static final URI RUBYGEMS_URI = URI.create("http://production.cf.rubygems.org/rubygems/rubygems-1.3.7.tgz");
public static Statement installRubyGems() {
return new StatementList(//
exec("("), //
extractTargzAndFlattenIntoDirectory(RUBYGEMS_URI, "/tmp/rubygems"), //
exec("{cd} /tmp/rubygems"), //
exec("ruby setup.rb --no-format-executable"), //
exec("{rm} -fr /tmp/rubygems"), //
exec(")"));
}
public InstallRuby() {
super(call("setupPublicCurl"), call("installRuby"), installRubyGems());
}
@Override
public String render(OsFamily family) {
if (family == OsFamily.WINDOWS) {
throw new UnsupportedOperationException("windows not yet implemented");
}
return super.render(family);
}
}

View File

@ -1,27 +1,5 @@
function installChefGems() { function installChefGems() {
if [ ! -f /usr/bin/chef-client ]; then if ! hash chef-client 2>/dev/null; then
if which dpkg &> /dev/null; then
apt-get-update
apt-get install -y ruby ruby1.8-dev build-essential wget libruby-extras libruby1.8-extras
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++ kernel-devel automake autoconf wget
else
abort "we only support apt-get and yum right now... please contribute"
fi
(
mkdir -p /tmp/bootchef
cd /tmp/bootchef
wget http://production.cf.rubygems.org/rubygems/rubygems-1.3.7.tgz
tar zxf rubygems-1.3.7.tgz
cd rubygems-1.3.7
ruby setup.rb --no-format-executable
rm -fr /tmp/bootchef
)
if which rpm &> /dev/null; then if which rpm &> /dev/null; then
#Install gems provided by libruby-extras deb package (based on https://launchpad.net/ubuntu/precise/+package/libruby-extras) #Install gems provided by libruby-extras deb package (based on https://launchpad.net/ubuntu/precise/+package/libruby-extras)
/usr/bin/gem install cmdparse daemons log4r mmap ncurses --no-rdoc --no-ri --verbose /usr/bin/gem install cmdparse daemons log4r mmap ncurses --no-rdoc --no-ri --verbose

View File

@ -0,0 +1,17 @@
function installRuby() {
if ! hash ruby 2>/dev/null; then
if which dpkg &> /dev/null; then
apt-get-update
apt-get install -y ruby ruby1.8-dev build-essential wget libruby-extras libruby1.8-extras
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++ kernel-devel automake autoconf wget
else
abort "we only support apt-get and yum right now... please contribute"
fi
fi
}

View File

@ -20,10 +20,15 @@ package org.jclouds.scriptbuilder.statements.chef;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import java.io.IOException;
import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.OsFamily;
import org.jclouds.scriptbuilder.domain.ShellToken;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.io.Resources;
@Test(groups = "unit", testName = "ChefSoloTest") @Test(groups = "unit", testName = "ChefSoloTest")
public class ChefSoloTest { public class ChefSoloTest {
@ -43,34 +48,42 @@ public class ChefSoloTest {
ChefSolo.builder().cookbooksArchiveLocation("/tmp/cookbooks").build().render(OsFamily.WINDOWS); ChefSolo.builder().cookbooksArchiveLocation("/tmp/cookbooks").build().render(OsFamily.WINDOWS);
} }
public void testChefSoloWithCookbooksLocation() { public void testChefSoloWithCookbooksLocation() throws IOException {
String script = ChefSolo.builder().cookbooksArchiveLocation("/tmp/cookbooks").build().render(OsFamily.UNIX); String script = ChefSolo.builder().cookbooksArchiveLocation("/tmp/cookbooks").build().render(OsFamily.UNIX);
assertEquals(script, assertEquals(
"setupPublicCurl || return 1\ninstallChefGems || return 1\nchef-solo -N `hostname` -r /tmp/cookbooks\n"); 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");
} }
public void testChefSoloWithCookbooksLocationAndSingleRecipe() { public void testChefSoloWithCookbooksLocationAndSingleRecipe() throws IOException {
String script = ChefSolo.builder().cookbooksArchiveLocation("/tmp/cookbooks").installRecipe("apache2").build() String script = ChefSolo.builder().cookbooksArchiveLocation("/tmp/cookbooks").installRecipe("apache2").build()
.render(OsFamily.UNIX); .render(OsFamily.UNIX);
assertEquals( assertEquals(
script, script,
"setupPublicCurl || return 1\ninstallChefGems || return 1\nchef-solo -N `hostname` -r /tmp/cookbooks -o recipe[apache2]\n"); 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");
} }
public void testChefSoloWithCookbooksLocationAndMultipleRecipes() { public void testChefSoloWithCookbooksLocationAndMultipleRecipes() throws IOException {
String script = ChefSolo.builder().cookbooksArchiveLocation("/tmp/cookbooks").installRecipe("apache2") String script = ChefSolo.builder().cookbooksArchiveLocation("/tmp/cookbooks").installRecipe("apache2")
.installRecipe("mysql").build().render(OsFamily.UNIX); .installRecipe("mysql").build().render(OsFamily.UNIX);
assertEquals( assertEquals(
script, script,
"setupPublicCurl || return 1\ninstallChefGems || return 1\nchef-solo -N `hostname` -r /tmp/cookbooks -o recipe[apache2],recipe[mysql]\n"); 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");
} }
public void testChefSoloWithCookbooksLocationAndMultipleRecipesInList() { public void testChefSoloWithCookbooksLocationAndMultipleRecipesInList() throws IOException {
String script = ChefSolo.builder().cookbooksArchiveLocation("/tmp/cookbooks") String script = ChefSolo.builder().cookbooksArchiveLocation("/tmp/cookbooks")
.installRecipes(ImmutableList.<String> of("apache2", "mysql")).build().render(OsFamily.UNIX); .installRecipes(ImmutableList.<String> of("apache2", "mysql")).build().render(OsFamily.UNIX);
assertEquals( assertEquals(
script, script,
"setupPublicCurl || return 1\ninstallChefGems || return 1\nchef-solo -N `hostname` -r /tmp/cookbooks -o recipe[apache2],recipe[mysql]\n"); 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");
} }
} }

View File

@ -39,9 +39,11 @@ public class InstallChefGemsTest {
new InstallChefGems().render(OsFamily.WINDOWS); new InstallChefGems().render(OsFamily.WINDOWS);
} }
public void installChefGemsUnix() { public void installChefGemsUnix() throws IOException {
assertEquals(new InstallChefGems().render(OsFamily.UNIX), assertEquals(
"setupPublicCurl || return 1\ninstallChefGems || return 1\n"); new InstallChefGems().render(OsFamily.UNIX),
Resources.toString(Resources.getResource("test_install_ruby." + ShellToken.SH.to(OsFamily.UNIX)),
Charsets.UTF_8) + "installChefGems || return 1\n");
} }
public void installChefGemsUnixInScriptBuilderSourcesSetupPublicCurl() throws IOException { public void installChefGemsUnixInScriptBuilderSourcesSetupPublicCurl() throws IOException {

View File

@ -0,0 +1,53 @@
/**
* 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.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;
@Test(groups = "unit", testName = "InstallRubyTest")
public class InstallRubyTest {
@Test(expectedExceptions = UnsupportedOperationException.class,
expectedExceptionsMessageRegExp = "windows not yet implemented")
public void installRubyInWindows() {
new InstallRuby().render(OsFamily.WINDOWS);
}
public void installRubyUnix() throws IOException {
assertEquals(new InstallRuby().render(OsFamily.UNIX), Resources.toString(
Resources.getResource("test_install_ruby." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8));
}
public void installRubyUnixInScriptBuilderSourcesSetupPublicCurl() throws IOException {
assertEquals(InitScript.builder().name("install_ruby").run(new InstallRuby()).build().render(OsFamily.UNIX),
Resources.toString(
Resources.getResource("test_install_ruby_scriptbuilder." + ShellToken.SH.to(OsFamily.UNIX)),
Charsets.UTF_8));
}
}

View File

@ -134,8 +134,8 @@ function setupPublicCurl() {
ensure_can_resolve_public_dns ensure_can_resolve_public_dns
return 0 return 0
} }
function installChefGems() { function installRuby() {
if [ ! -f /usr/bin/chef-client ]; then if ! hash ruby 2>/dev/null; then
if which dpkg &> /dev/null; then if which dpkg &> /dev/null; then
apt-get-update apt-get-update
apt-get install -y ruby ruby1.8-dev build-essential wget libruby-extras libruby1.8-extras apt-get install -y ruby ruby1.8-dev build-essential wget libruby-extras libruby1.8-extras
@ -149,15 +149,10 @@ function installChefGems() {
else else
abort "we only support apt-get and yum right now... please contribute" abort "we only support apt-get and yum right now... please contribute"
fi fi
( fi
mkdir -p /tmp/bootchef }
cd /tmp/bootchef function installChefGems() {
wget http://production.cf.rubygems.org/rubygems/rubygems-1.3.7.tgz if ! hash chef-client 2>/dev/null; then
tar zxf rubygems-1.3.7.tgz
cd rubygems-1.3.7
ruby setup.rb --no-format-executable
rm -fr /tmp/bootchef
)
if which rpm &> /dev/null; then if which rpm &> /dev/null; then
#Install gems provided by libruby-extras deb package (based on https://launchpad.net/ubuntu/precise/+package/libruby-extras) #Install gems provided by libruby-extras deb package (based on https://launchpad.net/ubuntu/precise/+package/libruby-extras)
/usr/bin/gem install cmdparse daemons log4r mmap ncurses --no-rdoc --no-ri --verbose /usr/bin/gem install cmdparse daemons log4r mmap ncurses --no-rdoc --no-ri --verbose
@ -174,6 +169,17 @@ END_OF_JCLOUDS_SCRIPT
rm -f $INSTANCE_HOME/rc rm -f $INSTANCE_HOME/rc
trap 'echo $?>$INSTANCE_HOME/rc' 0 1 2 3 15 trap 'echo $?>$INSTANCE_HOME/rc' 0 1 2 3 15
setupPublicCurl || exit 1 setupPublicCurl || exit 1
installRuby || exit 1
(
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.3.7.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
)
installChefGems || exit 1 installChefGems || exit 1

View File

@ -0,0 +1,12 @@
setupPublicCurl || return 1
installRuby || return 1
(
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.3.7.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
)

View File

@ -0,0 +1,228 @@
#!/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_ruby"
export INSTANCE_HOME="/tmp/$INSTANCE_NAME"
export LOG_DIR="$INSTANCE_HOME"
return $?
}
function install_ruby {
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_ruby || exit 1
mkdir -p $INSTANCE_HOME
# create runscript header
cat > $INSTANCE_HOME/install_ruby.sh <<-'END_OF_JCLOUDS_SCRIPT'
#!/bin/bash
set +u
shopt -s xpg_echo
shopt -s expand_aliases
PROMPT_COMMAND='echo -ne \"\033]0;install_ruby\007\"'
export PATH=/usr/ucb/bin:/bin:/sbin:/usr/bin:/usr/sbin
export INSTANCE_NAME='install_ruby'
END_OF_JCLOUDS_SCRIPT
cat >> $INSTANCE_HOME/install_ruby.sh <<-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_ruby.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
local pkg=$2
hash $cmd 2>/dev/null || ( apt-get-update && apt-get-install $pkg )
}
function ensure_cmd_or_install_package_yum(){
local cmd=$1
local pkg=$2
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() {
egrep -q `hostname` /etc/hosts || awk -v hostname=`hostname` 'END { print $1" "hostname }' /proc/net/arp >> /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 ruby1.8-dev build-essential wget libruby-extras libruby1.8-extras
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++ kernel-devel automake autoconf wget
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
cat >> $INSTANCE_HOME/install_ruby.sh <<-'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
(
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.3.7.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
)
END_OF_JCLOUDS_SCRIPT
# add runscript footer
cat >> $INSTANCE_HOME/install_ruby.sh <<-'END_OF_JCLOUDS_SCRIPT'
exit $?
END_OF_JCLOUDS_SCRIPT
chmod u+x $INSTANCE_HOME/install_ruby.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 $?