5
\$\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
\$\endgroup\$
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() {
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.