#!/bin/bash
TMP_DIR=${TMP_DIR:-/tmp}

# This script starts a Percona Toolkit sandbox sever.  sandbox/test-env
# uses it, and many tests use it to create special, temporary sandbox
# servers.  The actual startup is done by ${TMP_DIR}/PORT/start, which this
# script calls after doing a bunch of sanity checks.
#
# Exit 0 if the sandbox server started ok, else 1 and debug_sandbox()
# is caleld to print some info to STDERR about what state the sandbox
# server might have been in.

die() {
    echo $1 >&2
    exit 1
}

debug_sandbox() {
    set -x
    local port="$1"
    ps x | grep mysql >&2
    if [ -d "${TMP_DIR}/$port" ]; then
        ls -lh ${TMP_DIR}/$port/* >&2
        cat ${TMP_DIR}/$port/data/mysqld.log >&2
        tail -n 100 ${TMP_DIR}/$port/data/genlog >&2
    else
        echo "${TMP_DIR}/$port does not exist" >&2
    fi
}

mysql_upgrade_on() {
    local cnf_file="$1"
    local upgrade="$PERCONA_TOOLKIT_SANDBOX/bin/mysql_upgrade"

    $upgrade --defaults-file=$cnf_file --skip-write-binlog
}

make_sandbox() {
    # Make the sandbox dir and extract the base files.
    rm -rf ${TMP_DIR}/$port || die "Failed to rm ${TMP_DIR}/$port"
    mkdir ${TMP_DIR}/$port  || die "Failed to mkdir ${TMP_DIR}/$port"
    local generating_database=0

    cp $PERCONA_TOOLKIT_BRANCH/sandbox/servers/${APP#mysql}/$version/my.sandbox.cnf ${TMP_DIR}/$port
    if [ -e $PERCONA_TOOLKIT_BRANCH/sandbox/servers/${APP#mysql}/$version/data.tar.gz ]; then
        tar xzf $PERCONA_TOOLKIT_BRANCH/sandbox/servers/${APP#mysql}/$version/data.tar.gz -C ${TMP_DIR}/$port
    else
        generating_database=1
    fi

    for script in "$PERCONA_TOOLKIT_BRANCH/sandbox/servers/"*; do
        if [ -f $script ]; then
            cp $script ${TMP_DIR}/$port
        fi
    done

    if [ "${type}" = "cluster" ]; then
        cp $PERCONA_TOOLKIT_BRANCH/sandbox/servers/pxc/$version/my.sandbox.cnf ${TMP_DIR}/$port

        local libgalera="$PERCONA_TOOLKIT_SANDBOX/lib/libgalera_smm.so"
        local cluster_name="${CLUSTER_NAME:-"pt_sandbox_cluster"}"
        local cluster_address="gcomm://"
        local listen_port=$(($port + 10))
        local receive_port=$(($port + 20))
        if [ -n "${source_port}" ]; then
            local source_listen_port=$(($source_port + 10))
            cluster_address="gcomm://$ip:$source_listen_port"

            local this_listen_port=$(($port + 10))
            local this_cluster_address="gcomm://$ip:$this_listen_port"
            sed -e "s!gcomm://\$!$this_cluster_address!g" -i.bak "${TMP_DIR}/$source_port/my.sandbox.cnf"
        fi

        sed -e "s/ADDR/$ip/g" -i.bak "${TMP_DIR}/$port/my.sandbox.cnf"
        sed -e "s!CLUSTER_AD!$cluster_address!g" -i.bak "${TMP_DIR}/$port/my.sandbox.cnf"
        sed -e "s/CLUSTER_NAME/$cluster_name/g" -i.bak "${TMP_DIR}/$port/my.sandbox.cnf"
        sed -e "s/RECEIVE_PRT/$receive_port/g" -i.bak "${TMP_DIR}/$port/my.sandbox.cnf"
        sed -e "s/LISTEN_PRT/$listen_port/g" -i.bak "${TMP_DIR}/$port/my.sandbox.cnf"
        sed -e "s!LIBGALERA!$libgalera!g" -i.bak "${TMP_DIR}/$port/my.sandbox.cnf"
    fi

    for file in `grep -l PORT ${TMP_DIR}/$port/* 2>/dev/null`; do
        sed -e "s/PORT/$port/g" -i.bak $file
        sed -e "s!TMP_DIR!$TMP_DIR!g" -i.bak $file
        # Use ! instead of / because the replacement has / (it's a directory)
        sed -e "s!PERCONA_TOOLKIT_SANDBOX!$PERCONA_TOOLKIT_SANDBOX!g" -i.bak $file
        sed -e "s!MYSQLD!$mysqld!g" -i.bak $file
    done
    rm ${TMP_DIR}/$port/*.bak >/dev/null 2>&1

    if [ -n "$BINLOG_FORMAT" ]; then
        echo "binlog-format=$BINLOG_FORMAT" >> ${TMP_DIR}/$port/my.sandbox.cnf
    fi
    if [ -n "$REPLICA_EXEC_MODE" ]; then
      echo "${REPLICA_NAME}_exec_mode=$REPLICA_EXEC_MODE" >> ${TMP_DIR}/$port/my.sandbox.cnf
    fi
    if [ -n "$SQL_MODE" ]; then
        echo "sql-mode=$SQL_MODE" >> ${TMP_DIR}/$port/my.sandbox.cnf
    fi
    if [ -n "$GENLOG" ]; then
        echo "log=genlog" >> ${TMP_DIR}/$port/my.sandbox.cnf
    fi
    if [ -n "$SKIP_INNODB" ]; then
        echo "skip-innodb" >> ${TMP_DIR}/$port/my.sandbox.cnf
        echo "default-storage-engine=myisam" >> ${TMP_DIR}/$port/my.sandbox.cnf
        if [ "$version" ">" "5.5" ]; then
            echo "default-tmp-storage-engine=myisam" >> ${TMP_DIR}/$port/my.sandbox.cnf
        fi
    fi
    if [ -n "$MODE_ANSI" ]; then
        echo "sql_mode=ansi" >> ${TMP_DIR}/$port/my.sandbox.cnf
    fi
    if [ -n "$MULTIPLE_BUFFER_POOLS" ]; then
        echo "innodb_buffer_pool_instances=$MULTIPLE_BUFFER_POOLS" >> ${TMP_DIR}/$port/my.sandbox.cnf
    fi
    if [ -n "$LOCAL_INFILE" ]; then
        echo "local-infile=$LOCAL_INFILE" >> ${TMP_DIR}/$port/my.sandbox.cnf
    fi
    if [ -n "$QUERY_CACHE_SIZE" ]; then
        echo "query_cache_type=1" >> ${TMP_DIR}/$port/my.sandbox.cnf
        echo "query_cache_size=$QUERY_CACHE_SIZE" >> ${TMP_DIR}/$port/my.sandbox.cnf
    fi

    if [ -n "$GTID" ]; then
        if [ "$version" "<" '5.7' ]; then
            echo "gtid_mode=on" >> ${TMP_DIR}/$port/my.sandbox.cnf
        fi
        if [ ! "$version" "<" '5.7' ]; then
            echo "gtid_mode=ON_PERMISSIVE" >> ${TMP_DIR}/$port/my.sandbox.cnf
        fi
        echo "enforce_gtid_consistency" >> ${TMP_DIR}/$port/my.sandbox.cnf
    fi
    if [ -n "$REPLICATION_THREADS" ]; then
        echo "${REPLICA_NAME}_parallel_workers=$REPLICATION_THREADS" >> ${TMP_DIR}/$port/my.sandbox.cnf
    fi

    if [ -n "$EXTRA_DEFAULTS_FILE" ]; then
        cat "$EXTRA_DEFAULTS_FILE" >> ${TMP_DIR}/$port/my.sandbox.cnf
    fi

    if [ $generating_database -eq 0 ]; then
        # If the sandbox is a replica, set it read_only.
        if [ "$type" = "replica" ]; then
            echo "read_only" >> ${TMP_DIR}/$port/my.sandbox.cnf
        fi
    fi

    if [ $generating_database -eq 1 ]; then
        echo "Creating default databases ..."
        if [ "$version" ">" "5.6" ]; then
            rm -f ${TMP_DIR}/empty-defaults.txt
            touch ${TMP_DIR}/empty-defaults.txt
            rm -rf ${TMP_DIR}/$port/data
            $PERCONA_TOOLKIT_SANDBOX/$mysqld --defaults-file=${TMP_DIR}/${port}/my.sandbox.cnf --initialize
        else
            # MySQL up to 5.6 needs mysql_install_db
            # Support for it is a TODO
            echo "For MySQL < 5.7 data.tar.gz is required to be in $PERCONA_TOOLKIT_BRANCH/sandbox/servers/${APP#mysql}/$version/"
            exit 1
        fi

        init_file="${TMP_DIR}/$port/mysql-init"
        rm -rf $init_file

        $PERCONA_TOOLKIT_BRANCH/util/version_cmp $minor_version 5.7.5
        if [ $? -eq 2 ]; then
            echo "CREATE USER IF NOT EXISTS 'msandbox'@'%';" > $init_file
            echo "ALTER USER 'msandbox'@'%' IDENTIFIED BY 'msandbox' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;" >> $init_file
            echo "GRANT ALL PRIVILEGES ON *.* TO 'msandbox'@'%';" >> $init_file
            echo "-- Grants for 'root'@'localhost'" >> $init_file
            echo "CREATE USER IF NOT EXISTS 'root'@'localhost';" >> $init_file
            echo "ALTER USER 'root'@'localhost' IDENTIFIED BY 'msandbox' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;" >> $init_file
            echo "GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION;" >> $init_file
            echo "GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION;" >> $init_file
            echo "FLUSH PRIVILEGES;" >> $init_file
        else
            echo "SET PASSWORD FOR 'root'@'localhost' = PASSWORD('msandbox');" > $init_file
            echo "GRANT ALL PRIVILEGES ON *.* TO 'msandbox'@'localhost' IDENTIFIED BY 'msandbox';" >> $init_file
            echo "GRANT ALL PRIVILEGES ON *.* TO 'msandbox'@'127.0.0.1' IDENTIFIED BY 'msandbox';" >> $init_file
            echo "FLUSH PRIVILEGES;" >> $init_file
        fi

        if [ "$type" = "replica" ]; then
            echo "FLUSH TABLES WITH READ LOCK;" >> $init_file
            echo "SET GLOBAL read_only = ON;" >> $init_file
            echo "UNLOCK TABLES;" >> $init_file
        fi
    fi

    # Start the sandbox and check that it has InnoDB.
    ${TMP_DIR}/$port/start
    if [ $? -eq 0 ]; then
        if [ -z "$SKIP_INNODB" ]; then
            ${TMP_DIR}/$port/use -e 'SHOW /*!40100 ENGINE*/ INNODB STATUS' | grep 'INNODB MONITOR OUTPUT' >/dev/null 2>&1
            # grep exits 0 if lines are found
            if [ $? -ne 0 ]; then
                echo "Sandbox $type $port doesn't have InnoDB." >&2
                debug_sandbox $port
                exit 1
            fi

            for sql in "$PERCONA_TOOLKIT_BRANCH/sandbox/servers/${APP#mysql}/$version/"*.sql; do
                [ -f "$sql" ] && ${TMP_DIR}/$port/use < $sql
            done
        fi

        # create sys schema (if exists and is desired)
        if [ -n "$SYS_SCHEMA" ]; then
            local sys_dir="$PERCONA_TOOLKIT_BRANCH/sandbox/servers/${APP#mysql}/$version/sys"
            cd "$sys_dir"
            [ -f "$sys_dir/sys.sql" ] && ${TMP_DIR}/$port/use < "$sys_dir/sys.sql"
        fi
    else
        echo "Sandbox $type $port failed to start." >&2
        debug_sandbox $port
        exit 1
    fi


    ${TMP_DIR}/$port/use -e "CREATE DATABASE IF NOT EXISTS percona_test";
    ${TMP_DIR}/$port/use -e "CREATE TABLE IF NOT EXISTS percona_test.sentinel (id INT PRIMARY KEY, ping VARCHAR(64) NOT NULL DEFAULT '')";

    if [ -n "${MYSQL_UPGRADE:-""}" ]; then
        mysql_upgrade_on ${TMP_DIR}/$port/my.sandbox.cnf
    fi

    # If the sandbox is a replica, start the replica.
    if [ "$type" = "replica" ]; then
        ${TMP_DIR}/$port/use -e "change ${CHANGE_SOURCE_NAME} to ${SOURCE_NAME}_host='127.0.0.1', ${SOURCE_NAME}_user='msandbox', ${SOURCE_NAME}_password='msandbox', ${SOURCE_NAME}_port=$source_port, ${SOURCE_NAME}_SSL=1"
        ${TMP_DIR}/$port/use -e "start ${REPLICA_NAME}"
    fi

    if [ -x "$PERCONA_TOOLKIT_SANDBOX/bin/ps-admin" ]; then
        # try to enable RocksDB. Only available on Percona Server 5.7.19+
        if [ "$version" > "5.6" -a -n "$ENABLE_ROCKSDB" ]; then
            sudo rm -f /tmp/ps-admin.err
            $PERCONA_TOOLKIT_SANDBOX/bin/ps-admin --enable-rocksdb -u root -pmsandbox -h 127.1 -P $port
        fi
        if [ -n "$ENABLE_TOKUDB" ]; then
            $PERCONA_TOOLKIT_BRANCH/util/version_cmp $minor_version "8.0.28-19"
            if [[ "$?" < "2" ]] && [[ "$version" > "5.6" ]]; then
                sudo rm -f /tmp/ps-admin.err
                sudo $PERCONA_TOOLKIT_SANDBOX/bin/ps-admin --enable-tokudb -u root -pmsandbox -h 127.1 -P $port
            fi
        fi
    fi

    return 0
}

# ###########################################################################
# Sanity check the cmd line options.
# ###########################################################################
if [ $# -lt 2 ]; then
    die "Usage: start-sandbox source|replica|source-source|cluster|channels port [source port]"
fi

type=$1         # source, replica or source-source
port=$2         # sandbox port number, e.g. 12345
source_port=$3  # source port if replica or source-source
enable_tokudb=''


if [ "$type" != "source" ] && [ "$type" != "replica" ] && [ "$type" != "source-source" ] && [ "$type" != "cluster" ] && [ "$type" != "channels" ]; then
    die "Invalid sandbox type: $type.  Valid types are source, replica, source-source, cluster, and channels."
fi

if [ $port -le 1024 ]; then
    die "Invalid port: $port.  Ports must be > 1024."
fi

if [ "$type" = "replica" -o "$type" = "source-source" ] && [ -z "$source_port" ]; then
    die "No source port given for the $type."
fi

# If creating a replica, the source must exist first.  Not true for creating
# a source-source though.
if [ "$type" = "replica" ] && [ ! -d "${TMP_DIR}/$source_port" ]; then
    die "Source sandbox does not exist: ${TMP_DIR}/$source_port"
fi

# ###########################################################################
# Sanity check the environment.
# ###########################################################################
if [ -z "$PERCONA_TOOLKIT_BRANCH" ]; then
    die "PERCONA_TOOLKIT_BRANCH environment variable is not set."
fi

if [ ! -d "$PERCONA_TOOLKIT_BRANCH" ]; then
    die "Invalid PERCONA_TOOLKIT_BRANCH directory: $PERCONA_TOOLKIT_BRANCH"
fi

cd $PERCONA_TOOLKIT_BRANCH/sandbox

# This script is usually called by test-env which discovers and
# sets PERCONA_TOOLKIT_SANDBOX.  If this script is called directly,
# then the caller is responsible for setting PERCONA_TOOLKIT_SANDBOX.
# PERCONA_TOOLKIT_SANDBOX points to a base directory containing the
# MySQL executables like PERCONA_TOOLKIT_SANDBOX/bin/mysqld_safe.

if [ -z "$PERCONA_TOOLKIT_SANDBOX" ]; then
    PERCONA_TOOLKIT_SANDBOX=`./test-env checkconfig | grep PERCONA_TOOLKIT_SANDBOX | cut -d= -f2 | awk '{print $1}'`
    if [ -z "$PERCONA_TOOLKIT_SANDBOX" ]; then
        die "PERCONA_TOOLKIT_SANDBOX environment variable is not set."
    fi
fi

# ###########################################################################
# Get server version.
# ###########################################################################
if [ -x "$PERCONA_TOOLKIT_SANDBOX/bin/mysqld" ]; then
    mysqld="bin/mysqld"
elif [ -x "$PERCONA_TOOLKIT_SANDBOX/sbin/mysqld" ]; then
    mysqld="sbin/mysqld"
elif [ -x "$PERCONA_TOOLKIT_SANDBOX/libexec/mysqld" ]; then
    mysqld="libexec/mysqld"
else
    die "Cannot find executable mysqld in $PERCONA_TOOLKIT_SANDBOX/bin, $PERCONA_TOOLKIT_SANDBOX/sbin or $PERCONA_TOOLKIT_SANDBOX/libexec."
fi

APP="${FORK:-"mysql"}"

if [ $type = "cluster" -o $APP = "pxc" ]; then
    # disabled for now because used perl module is not available everywhere and in some distros it returns ipv6 address
    #  ip=$(perl -MNet::Address::IP::Local -le 'print Net::Address::IP::Local->public')
    ip=$(hostname -i | cut -d" " -f2)
    version=`$PERCONA_TOOLKIT_SANDBOX/$mysqld -V --query_cache_size=0 --query_cache_type=0  --bind-address $ip 2>/dev/null | awk '{print $3}' | cut -d. -f 1,2`;
    minor_version=`$PERCONA_TOOLKIT_SANDBOX/$mysqld -V --query_cache_size=0 --query_cache_type=0  --bind-address $ip 2>/dev/null | awk '{print $3}' | cut -d. -f 1,2,3 | cut -d- -f1`;
else
    version=`$PERCONA_TOOLKIT_SANDBOX/$mysqld -V 2>/dev/null | awk '{print $3}' | cut -d. -f 1,2`;
    minor_version=`$PERCONA_TOOLKIT_SANDBOX/$mysqld -V 2>/dev/null | awk '{print $3}' | cut -d. -f 1,2,3 | cut -d- -f1`;
fi

SOURCE_NAME="master"
CHANGE_SOURCE_NAME="master"
REPLICA_NAME="slave"
if [ "$version" '>' "8.1" ] && [ "$APP" '!=' "mariadb" ]; then
   SOURCE_NAME="source"
   CHANGE_SOURCE_NAME="replication source"
   REPLICA_NAME="replica"
fi

if [ ! -d "$PERCONA_TOOLKIT_BRANCH/sandbox/servers/${APP#mysql}/$version" ]; then
    die "$PERCONA_TOOLKIT_BRANCH/sandbox/servers/${APP#mysql}/$version does not exist."
fi

# ###########################################################################
# Start and configure the sandbox server.
# ###########################################################################
PIDFILE="${TMP_DIR}/$port/data/mysql_sandbox$port.pid"

if [ -f $PIDFILE ]; then
    echo "Sandbox $port already started (found pid file $PIDFILE)"
else
    make_sandbox
    # make_sandbox has started replica and set read_only if necessary.

    # If the sandbox is a source-source, start the second source and replica the
    # two together.
    if [ "$type" = "source-source" ]; then
        mm1_port=$port
        mm2_port=$source_port
        port=$source_port  # make_sandbox uses $port
        make_sandbox

        # Replica mm2 -> mm1
        ${TMP_DIR}/$mm2_port/use -e "change ${CHANGE_SOURCE_NAME} to ${SOURCE_NAME}_host='127.0.0.1', ${SOURCE_NAME}_log_file='mysql-bin.000001', ${SOURCE_NAME}_user='msandbox', ${SOURCE_NAME}_password='msandbox', ${SOURCE_NAME}_port=$mm1_port, ${SOURCE_NAME}_SSL=1"
        ${TMP_DIR}/$mm2_port/use -e "start ${REPLICA_NAME}"

        # Replica mm1 -> mm2
        ${TMP_DIR}/$mm1_port/use -e "change ${CHANGE_SOURCE_NAME} to ${SOURCE_NAME}_host='127.0.0.1', ${SOURCE_NAME}_log_file='mysql-bin.000001', ${SOURCE_NAME}_user='msandbox', ${SOURCE_NAME}_password='msandbox', ${SOURCE_NAME}_port=$mm2_port, ${SOURCE_NAME}_SSL=1"
        ${TMP_DIR}/$mm1_port/use -e "start ${REPLICA_NAME}"
    fi
fi

exit $?
