I wrote a pretty basic setup script that handles most of the setup for a Three-server solution. The solution and setup file are available on GitHub
The three servers are Redis, HAProxy and Node / Cron.
All three options handle only solution specific stuff (not including installing the frameworks)
I felt like my usage of select
statements was a little over the top, and newline as a constant seemed a little hacky, but nonetheless, it works.
n="
"
# From http://stackoverflow.com/a/246128
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
pass (){
echo 1ドル | sha256sum | head -c 64
}
# Redis
redis (){
ulimit -n 10032
slave=false;
echo "Enter the desired password for the Redis instance:"
read -s password
password=$(pass $password)
echo "Is this a slave?"
select yn in "Yes" "No"; do
case $yn in
Yes ) slave=1; break;;
No ) slave=0; break;;
esac
done
slave_text="";
if [[ $slave -eq 1 ]]; then
echo "Enter the Master IP address:"
read master_ip
echo "Enter the Redis Master password:"
read -s master_password
slave_text="slaveof $master_ip 6379
masterauth $master_password $n"
fi
echo "tcp-keepalive 60
port 6379
tcp-backlog 511
timeout 600
dir $DIR
supervised upstart
daemonize yes
#bind 127.0.0.1
maxmemory-policy noeviction
requirepass $password
$slave_text
" > "/etc/redis/redis.conf"
echo "The password for Redis is $password"
sudo service redis restart
}
# HAProxy
haproxy (){
stats=1;
stats_text="stats disable$n";
echo "Please enter the IP address of the web server:"
read initial_ip
echo "Please enter the port of the web server:"
read initial_port
i=1
web_servers="server web$i $initial_ip:$initial_port check$n"
more_web_servers=1;
while [ $more_web_servers -eq 1 ]
do
echo "Would you like to add another web server?"
select yn in "Yes" "No"; do
case $yn in
Yes )
echo "Please enter the IP address:"
read repeat_ip
echo "Please enter the port:"
read repeat_port;
((i++))
web_servers+=" server web$i $repeat_ip:$repeat_port check$n";
break;;
No ) more_web_servers=0; break;;
esac
done
done
echo "Do you want to turn HAProxy stats on?"
select yn in "Yes" "No"; do
case $yn in
Yes ) stats=1; break;;
No ) stats=0; break;;
esac
done
if [[ $stats -eq 1 ]]; then
echo "Enter the URL for stats:"
read stats_uri
echo "Please enter a user to access the stats page"
echo "Please enter the Username:"
read username
echo "Please enter the Password:"
read -s password
users="stats auth $username:$password$n"
more_users=1;
while [ $more_users -eq 1 ]
do
echo "Would you like to add another user?"
select yn in "Yes" "No"; do
case $yn in
Yes )
echo "Please enter the Username:"
read username
echo "Please enter the Password:"
read -s password
users+=" stats auth $username:$password$n"
break;;
No ) more_users=0; break;;
esac
done
done
stats_text="stats enable
stats uri $stats_uri
stats realm Strictly\ Private
$users"
fi
echo "global
log 127.0.0.1 local0 notice
maxconn 2000
user haproxy
group haproxy
defaults
log global
mode http
option httplog
option dontlognull
retries 3
option redispatch
timeout connect 5000
timeout client 10000
timeout server 10000
listen web
bind 0.0.0.0:80
timeout connect 5000
timeout client 10000
timeout server 10000
mode http
$stats_text
balance roundrobin
option httpclose
option forwardfor
$web_servers $n" > "/etc/haproxy/haproxy.cfg"
service haproxy restart
}
# web / cron
web (){
cron_dir="$DIR/dist/cron";
cron_content="#!/bin/bash
/usr/bin/node $cron_dir";
echo "Please enter the Redis Master IP:"
read redis_master_ip
echo "Please enter the Redis Master Auth password:"
read -s redis_master_password
echo "Please enter the HAProxy IP address:"
read HAProxy_IP
echo "{
\"redis\": {
\"current_environment\": \"prod\",
\"environments\": {
\"prod\": {
\"host\": \"$redis_master_ip\",
\"port\": \"6379\",
\"password\": \"$redis_master_password\"
}
}
},
\"haproxy\": {
\"IP\": \"$HAProxy_IP\"
}
}" > "$DIR/web.config.json"
npm install -g webpack gulp > /dev/null 2 >&1
npm install > /dev/null 2 >&1
echo "Before continuing, we need to build the solution.
Have you done this, or would you like to now? (yes or no)"
select yn in "Yes" "No"; do
case $yn in
Yes ) npm run install; break;;
No ) break;;
esac
done
if [ -f "$cron_dir/posts.js" ]
then
echo "30 * * * * $cron_content/posts.js > $DIR/posts.log" >> /etc/crontab
echo "Posts CRON job added."
fi
if [ -f "$cron_dir/sites.js" ]
then
echo "0 23 * * * $cron_content/sites.js > $DIR/sites.log" >> /etc/crontab
echo "Sites CRON job added."
fi
echo "start on runlevel [2345]
start on filesystem
exec $DIR/node_modules/.bin/forever start $DIR/dist/web/index.js -o $DIR/out.log" > "/etc/init/web.conf"
service web start
}
echo "StackNews setup script
-----------
Is this a Redis server, HAProxy server or web/cron server?"
select option in "Redis" "Web" "HAProxy"; do
case $option in
Redis ) redis; break;;
Web ) web; break;;
HAProxy ) haproxy; break;;
esac
done
1 Answer 1
shellcheck it!
There is this wonderful site called shellcheck.net that tells you all kinds of common mistakes.
The newline in a string
n=" "
n
is an unusual name for something that's a newline. How about newline
?
Here's another writing style that you might like:
n=$'\n'
For more details on this syntax, search for \$'
in man bash
.
Embedding variables in a string
The linebreak is odd in the middle of the string,
especially considering the $n
variable that you have:
slave_text="slaveof $master_ip 6379 masterauth $master_password $n"
I'm wondering if you didn't use $n
because you were not sure how to make it work. Here are some equivalent ways to write the statement on a single line:
slave_text="slaveof $master_ip 6379${n}masterauth $master_password $n"
slave_text="slaveof $master_ip 6379$n""masterauth $master_password $n"
Embedding variables in text
This is very tedious, as you had to escape all the "
:
echo "{ \"redis\": { \"current_environment\": \"prod\", \"environments\": { \"prod\": { \"host\": \"$redis_master_ip\", \"port\": \"6379\", \"password\": \"$redis_master_password\" } } }, \"haproxy\": { \"IP\": \"$HAProxy_IP\" } }" > "$DIR/web.config.json"
A simpler approach would have been to enclose in single quotes instead, and break out of it in front of variables, like this:
echo '{
"redis": {
"current_environment": "prod",
"environments": {
"prod": {
"host": "'$redis_master_ip'",
"port": "6379",
"password": "'$redis_master_password'"
}
}
},
"haproxy": {
"IP": "'$HAProxy_IP'"
}
}' > "$DIR/web.config.json"
But an even better way is to use a here-string:
cat << EOF > "$DIR/web.config.json"
{
"redis": {
"current_environment": "prod",
"environments": {
"prod": {
"host": "$redis_master_ip",
"port": "6379",
"password": "$redis_master_password"
}
}
},
"haproxy": {
"IP": "$HAProxy_IP"
}
}
EOF
Redirecting both stdout
and stderr
Instead of this:
npm install -g webpack gulp > /dev/null 2 >&1
A simpler way to redirect both stdout
and stderr
is this:
npm install -g webpack gulp &> /dev/null
The shebang
Don't forget to start your script with the correct shebang:
#!/usr/bin/env bash
Inconsistent use of [
and [[
In some places you use [
and in others [[
.
I think it will be slightly easier to read the script if you used a consistent writing style.
[
is considered obsolete, [[
is recommended.
Trailing ;
A ;
at the end of a line is pointless (unless it's part of a ;;
in a case
statement). I suggest to remove those.
Tedious -eq
The -eq
operator is kind of tedious here:
if [[ $slave -eq 1 ]]; then
A simple =
would work just as well, so I recommend to write that way.
Writing style
This is a bit unusual:
pass (){
A more common writing style is this:
pass() {