4
\$\begingroup\$

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
asked Jul 28, 2016 at 13:13
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

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() {
answered Aug 1, 2016 at 19:56
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.