From 30986b0f8525a6f988d03eb173289ce5204048e2 Mon Sep 17 00:00:00 2001 From: lework Date: Wed, 2 Dec 2020 13:42:02 +0800 Subject: [PATCH] add --- shell/ACS-ECS-GuestOS-Diganostic-for-linux.sh | 375 +++ shell/get_proc_mem.sh | 171 +- shell/{ => k8s}/getgcr.sh | 0 shell/k8s/k8s-app-info.sh | 189 ++ shell/k8s/k8s-backup.sh | 44 + shell/{ => k8s}/kube-logging.sh | 0 shell/library.sh | 11 +- shell/move_train.sh | 27 + shell/{ => ssl}/cfssl.sh | 0 shell/ssl/gen_ssl_certs.sh | 50 + shell/{ => ssl}/keystore.sh | 0 shell/util.sh | 2684 +++++++++++++++++ shell/yaml.sh | 28 + 13 files changed, 3546 insertions(+), 33 deletions(-) create mode 100644 shell/ACS-ECS-GuestOS-Diganostic-for-linux.sh rename shell/{ => k8s}/getgcr.sh (100%) create mode 100644 shell/k8s/k8s-app-info.sh create mode 100644 shell/k8s/k8s-backup.sh rename shell/{ => k8s}/kube-logging.sh (100%) create mode 100644 shell/move_train.sh rename shell/{ => ssl}/cfssl.sh (100%) create mode 100644 shell/ssl/gen_ssl_certs.sh rename shell/{ => ssl}/keystore.sh (100%) create mode 100644 shell/util.sh create mode 100644 shell/yaml.sh diff --git a/shell/ACS-ECS-GuestOS-Diganostic-for-linux.sh b/shell/ACS-ECS-GuestOS-Diganostic-for-linux.sh new file mode 100644 index 0000000..4c55ce3 --- /dev/null +++ b/shell/ACS-ECS-GuestOS-Diganostic-for-linux.sh @@ -0,0 +1,375 @@ +#!/bin/bash +set -u + +LOG_DIR=/var/log/diagnostic +LOG_FILE_NAME="i-uf63gv6j947wbfm1zodq20201104165109" +LOG_FILE=${LOG_DIR}/${LOG_FILE_NAME} +OSS_URL="" +OS_RELEASE="aliyun" +OS_BIG_VERSION='2' + +function check_fs() { + echo "###fs-state" + IFS_old=$IFS + IFS=$'\n' + for i in $(blkid) + do + blk=$(echo $i | awk -F: '{print $1}') + fs_type=$(echo $i | egrep -o "TYPE=\"ext[0-9]\"|TYPE=\"xfs\"" | egrep -o "ext[0-9]|xfs") + if [[ "${fs_type}" =~ "ext" ]] + then + echo ${blk} + fsck -n /dev/vda1 > /dev/null 2>&1; echo $? + elif [[ "${fs_type}" =~ "xfs" ]] + then + echo ${blk} + xfs_repair -n ${blk} > /dev/null 2>&1 ; echo $? + fi + done + IFS=$IFS_old +} + +function get_os() { + if ! test -f "/etc/os-release"; then + if test -f "/etc/redhat-release"; then + OS_RELEASE="centos" + else + OS_RELEASE="freebsd" + fi + + + match=$(awk -F'=' '/^VERSION_ID/ {gsub("\"","",$NF); print $NF}' /etc/os-release) + OS_BIG_VERSION=${match%%.*} + fi + + if grep "Ubuntu" "/etc/os-release"; then + OS_RELEASE="ubuntu" + fi + + if grep "Debian" "/etc/os-release"; then + OS_RELEASE="debian" + fi + + if grep "CentOS" "/etc/os-release"; then + OS_RELEASE="centos" + fi + + if grep "SLES" "/etc/os-release"; then + OS_RELEASE="suse" + fi + + if grep -i "CoreOS" "/etc/os-release"; then + OS_RELEASE="coreos" + fi + + if grep "Aliyun" "/etc/os-release"; then + OS_RELEASE="aliyun" + fi +} + + +function eth0_network_dhcp(){ + + network_service_array=("Networking" "NetworkManager" "systemd-networkd" "netplan" "wicked" "others") + network_service='${network_service[5]}' + net_process_exit=false + net_proto='static' + + #echo "***default" + #mac=$(curl -s --connect-timeout 2 --fail 100.100.100.200/latest/meta-data/network/interfaces/macs/) + #gateway=$(curl -s --connect-timeout 2 --fail 100.100.100.200/latest/meta-data/network/interfaces/macs/$mac/gateway) + + if [ "$OS_RELEASE"X == "centos"X ]; then + echo "***centos" + if [ "$OS_BIG_VERSION" == "7" ];then + if [[ $(systemctl is-active network.service) == 'active' ]];then + network_service=${network_service_array[0]} + elif [[ $(systemctl is-active NetworkManager) == 'active' ]];then + network_service=${network_service_array[1]} + elif [[ $(systemctl is-active systemd-networkd) == 'active' ]];then + network_service=${network_service_array[2]} + else + network_service=${network_service_array[5]} + fi + elif [ "$OS_BIG_VERSION" == "8" ];then + network_service=${network_service_array[1]} + else + network_service=${network_service_array[0]} + fi + + net_proto=$(grep "^BOOTPROTO=" /etc/sysconfig/network-scripts/ifcfg-eth0 | awk -F'=' '{print $2}') + elif [ "$OS_RELEASE"X == "aliyun"X ];then + echo "***aliyun" + network_service=${network_service_array[2]} + systemd_dir=/etc/systemd/network/*.network + for inet in `ls $systemd_dir`; + do + if grep -q "eth0" $inet && grep -q "DHCP=yes" $inet;then + net_proto="dhcp" + break + fi + done + + elif [ "$OS_RELEASE"X == "ubuntu"X ];then + echo "***ubuntu" + network_service=${network_service_array[2]} + net_proto="static" + if [ "$OS_BIG_VERSION" -ge 18 ];then + net_dir=/etc/netplan/*.yaml + for inet in `ls $netplan_dir`; + do + if grep -q "eth0" $inet && grep -q "dhcp4:[[:space:]]*yes" $inet;then + net_proto="dhcp" + break + fi + done + else + interface_cfg=/etc/network/interfaces + if grep -q "eth0[[:space:]]*inet[[:space:]]*dhcp" $interface_cfg;then + net_proto="dhcp" + fi + fi + elif [ "$OS_RELEASE"X == "debian"X ];then + echo "***debian" + network_service=${network_service_array[2]} + net_proto='static' + interface_cfg=/etc/network/interfaces + if grep -q "eth0[[:space:]]*inet[[:space:]]*dhcp" $interface_cfg;then + net_proto="dhcp" + fi + elif [ "$OS_RELEASE"X == "suse"X ];then + echo "***suse" + network_service=${network_service_array[4]} + net_proto='static' + sysconfig_cfg=/etc/sysconfig/network/ifcfg-eth0 + if grep -qE "^BOOTPROTO='dhcp4'|^BOOTPROTO='dhcp'" $sysconfig_cfg;then + net_proto='dhcp' + fi + else + echo "network_service:unknow" + echo "net_proto:unknow" + echo "net_process:unknow" + return + + fi + + if [[ $network_service == ${network_service_array[0]} ]];then + process="dhclient" + elif [[ $network_service == ${network_service_array[1]} ]];then + process="NetworkManager" + elif [[ $network_service == ${network_service_array[2]} ]];then + process="systemd-networkd" + elif [[ $network_service == ${network_service_array[4]} ]];then + process="wickedd" + fi + + ps aux |grep $process |grep -v grep >/dev/null + if [[ $? == 0 ]];then + net_process_exit=true + fi + + echo "network_service:$network_service" + echo "net_proto:$net_proto" + echo "net_process_exit:$net_process_exit" +} + +function get_configs() { + echo "##*problem_total_analyse" + + # check osinfo + echo "###osinfo" + if test -f "/etc/os-release"; then + cat /etc/os-release | egrep "^NAME=|^VERSION=" + else + echo "no os-release" + echo "no os-release" + fi + if test -f "/etc/redhat-release" ; then + echo "redhat-release:" $(cat /etc/redhat-release) + else + echo "no redhat-release" + fi + echo "uname: " $(uname -a) + echo "uname short\: " $(uname -r) + + # check the passwd format + echo "###dos-ff" + elf_pas="`cat /etc/passwd | hexdump |head -n 2|head -n 1 |awk '{print $NF}'|cut -c 1-2`" + elf_sha="`cat /etc/shadow | hexdump |head -n 2|head -n 1 |awk '{print $NF}'|cut -c 1-2`" + #elf_pam="`cat /etc/pam.d/* | hexdump |head -n 2|head -n 1 |awk '{print $NF}'|cut -c 1-2`" + if [ "elf_pas" != "3a" ];then + echo "/etc/passwd: ASCII text" + else + echo "/etc/passwd: ASCII text, with no line terminators" + fi + if [ "elf_sha" != "3a" ];then + echo "/etc/shadow: ASCII text" + else + echo "/etc/shadow: ASCII text, with no line terminators" + fi + + # check the limits + echo "###limits" + cat /etc/security/limits.conf | grep -Ev "^$|[#;]" + + # check the virtio driver exists + echo "###virtio-net-multiqueue" + for i in $(ip link | grep -E "^[0-9]+: .*:" -o | cut -d ":" -f 2 | grep -v lo); do + echo $i + ethtool -l $i 2>/dev/null | grep Combined + done + + # check eth0 newtork dhcp + echo "###eth0-network-dhcp" + eth0_network_dhcp + + + # check passwd only + echo "###passwd" + cat /etc/passwd + + echo "###cpu-top-5" + top -b -n 1 | grep "%Cpu(s):" + ps -eT -o%cpu,pid,tid,ppid,comm | grep -v CPU | sort -n -r | head -5 + + # check ssh permission format + echo "###ssh-perm" + if [ "$OS_RELEASE"X == "centos"X ]; then + echo "***centos" + ls -l /etc/passwd /etc/shadow /etc/group /etc/gshadow /var/empty/* /etc/securetty* /etc/security/* /etc/ssh/* + fi + + if [ "$OS_RELEASE"X == "ubuntu"X ]; then + echo "***ubuntu" + ls -l /etc/passwd /etc/shadow /etc/group /etc/gshadow /etc/securetty* /etc/security/* /etc/ssh/* + fi + + if [ "$OS_RELEASE"X == "debian"X ]; then + echo "***debian" + ls -l /etc/passwd /etc/shadow /etc/group /etc/gshadow /etc/securetty* /etc/security/* /etc/ssh/* + fi + if [ "$OS_RELEASE"X == "coreos"X ]; then + echo "***coreos" + ls -l /etc/passwd /etc/shadow /etc/group /etc/gshadow /var/empty/* /etc/securetty* /etc/security/* /etc/ssh/* + fi + + # check blkid + echo "###blkid" + blkid + + # check the softlink + echo "###softlink" + ls -l / | grep "\->" + + # check iptables + echo "###iptables" + + echo "***centos-5" + service iptables status + + echo "***centos-6" + service iptables status + + echo "***centos-7" + firewall-cmd --state + + echo "***centos-8" + firewall-cmd --state + + echo "***ubuntu" + ufw status + + echo "***coreos" + status="`systemctl status iptables 2>&1`" + echo "$status" + + echo "***default" + iptables -L + + # check the sysctl configuration + echo "###sysctl" + cat /etc/sysctl.conf | grep nr_hugepages + echo -n "net.ipv4.tcp_tw_recycle=" + cat /proc/sys/net/ipv4/tcp_tw_recycle + echo -n "net.ipv4.tcp_timestamps=" + cat /proc/sys/net/ipv4/tcp_timestamps + echo -n "fs.nr_open=" + cat /proc/sys/fs/nr_open + echo -n "net.ipv4.tcp_sack=" && cat /proc/sys/net/ipv4/tcp_sack + + # check fstab configuration + echo "###fstab" + if [ "$OS_RELEASE"X == "coreos"X ]; then + cat /etc/mtab | grep -v 'proc\|sys\|tmpfs\|securityfs\|cgroup\|devpts\|selinux\|debug\|mqueue\|huge\|pstore\|bpf' + else + cat /etc/fstab | grep -Ev "^$|[#;]" + fi + + + # check dmesg info + echo "###dmesg" + cat /proc/uptime + dmesg | grep "invoked oom-killer" | tail -n 1 + + # check the port usage + # echo "###port-usage" + # echo "***default" + # netstat -tapn | grep LISTEN | grep -E 'sshd' + # netstat -tapn | grep LISTEN | grep -E '0.0.0.0:80' + # netstat -tapn | grep LISTEN | grep -E '0.0.0.0:443' + # echo "***coreos" + # #coreos sshd hosts by systemd + # netstat -tapn | grep LISTEN | grep -E 'systemd' + # netstat -tapn | grep LISTEN | grep -E '0.0.0.0:80' + # netstat -tapn | grep LISTEN | grep -E '0.0.0.0:443' + + # check if the selinux on + echo "###selinux" + echo "***default" + getenforce + + echo "***ubuntu" + service selinux status > /dev/null; echo $? + echo "***debian-8" + service selinux status > /dev/null; echo $? + echo "***debian-9" + sestatus | grep "SELinux status" + echo "***debian-10" + sestatus | grep "SELinux status" + + # check the memroy info + echo "###meminfo" + cat /proc/meminfo | grep Hugepagesize + cat /proc/meminfo | grep MemTotal + + # check fs state + check_fs + + # check sshd-config + echo "###sshd-config" + cat /etc/ssh/sshd_config | egrep "PermitRootLogin|AllowUsers|AllowGroups|DenyUsers|DenyGroups" | egrep -v "^$|[#;]" + + # check inode usage + echo "###disk-inode" + df -i | egrep "/dev/x?vd" +} + + +# upload logs to OSS +function upload() { + cd $LOG_DIR + curl -i -q -X PUT -T ${LOG_FILE} ${OSS_URL} +} + +function rmlog() { + test -f ${LOG_FILE} && rm -f ${LOG_FILE} +} + +function main() { + test -e ${LOG_DIR} || mkdir -p ${LOG_DIR} + get_os + get_configs >${LOG_FILE} 2>&1 + upload +} + +main "$@" \ No newline at end of file diff --git a/shell/get_proc_mem.sh b/shell/get_proc_mem.sh index 430b495..538ad8c 100644 --- a/shell/get_proc_mem.sh +++ b/shell/get_proc_mem.sh @@ -1,16 +1,47 @@ #!/usr/bin/env bash - - -pid=$1 -retries="${2:-0}" -wait="${3:-1}" -pid_smaps="" - - -function get_meminfo() { - [ ! -f "/proc/${pid}/smaps" ] \ - && { echo "[Error] not found $pid smaps file."; echo "Usage: bash $0 Pid Retries Wait, like: bash$0 1234 100 5"; exit 1; } \ - || pid_smaps=$(cat /proc/${pid}/smaps) +################################################################### +#Script Name : get_proc_mem.sh +#Description : Get Process Memory information. +#Create Date : 2020-10-15 +#Author : lework +#Email : lework@yeah.net +################################################################### + + +[[ -n $DEBUG ]] && set -x || true +set -o errtrace # Make sure any error trap is inherited +set -o nounset # Disallow expansion of unset variables +set -o pipefail # Use last non-zero exit code in a pipeline + + +###################################################################################################### +# environment configuration +###################################################################################################### + +PID="${PID:-1}" +RETRIES="${RETRIES:-0}" +WAIT="${WAIT:-1}" + +COLOR_RED="${COLOR_RED:-\e[1;31m}" +COLOR_GREEN="${COLOR_GREEN:-\e[1;32m}" +COLOR_YELLOW="${COLOR_RED:-\e[1;33m}" +COLOR_BLUE="${COLOR_BLUE:-\e[1;34m}" +COLOR_PURPLE="${COLOR_PURPLE:-\e[1;35m}" +COLOR_CYAN="${COLOR_CYAN:-\e[1;36m}" +COLOR_GRAY="${COLOR_GRAY:-\e[1;90m}" +COLOR_OFF="${COLOR_OFF:-\e[0m}" +NOCOLOR="${NOCOLOR:-false}" + +###################################################################################################### +# function +###################################################################################################### + +function get::meminfo() { + + [ ! -f "/proc/${PID}/smaps" ] && { echo -e "${COLOR_RED}[Error]${COLOR_OFF} not found $PID smaps file!"; exit 1; } + + pid_smaps=$(cat /proc/${PID}/smaps) + [ "$pid_smaps" == "" ] && { echo -e "${COLOR_RED}[Error]${COLOR_OFF} /proc/${PID}/smaps is empty!"; exit 1; } mem_info=$(cat /proc/meminfo) @@ -29,19 +60,35 @@ function get_meminfo() { swap_pss=$(printf "%s" "${pid_smaps}" | awk '/^SwapPss/{sum += $2}END{print sum}') } -count=0 -while [ $count -lt $retries ] ; do - get_meminfo - echo "Date: $(date +'%Y-%m-%d %T') MemTotal: $((mem_total/1024))MB MemFree: $((mem_free/1024))MB MemAvailable: $((mem_available/1024))MB RSS: $((${rss}/1024))MB PSS: $((${pss}/1024))MB USS: $(( (${private_clean} + ${private_dirty}) /1024 ))MB" - sleep $wait - count=$(($count + 1)) -done +function get::pidinfo() { + echo -e "${COLOR_PURPLE} +Pid: ${PID} +Cmd: $(tr -d '\0' < /proc/${PID}/cmdline | cut -c1-80) +User: $(id -nu < /proc/${PID}/loginuid ) +Threads: $(awk '/Threads:/ {print $2}' /proc/${PID}/status) +File: /proc/${PID}/smaps +${COLOR_OFF}" -get_meminfo +} + +function get::meminfo_loop() { + local count=0 + get::pidinfo + while [ $count -lt $RETRIES ] ; do + get::meminfo + echo -e "Date: $(date +'%Y-%m-%d %T') ${COLOR_PURPLE}MemTotal: $((mem_total/1024))MB${COLOR_OFF} ${COLOR_GREEN}MemFree: $((mem_free/1024))MB${COLOR_OFF} ${COLOR_BLUE}MemAvailable: $((mem_available/1024))MB${COLOR_OFF} ${COLOR_YELLOW}RSS: $((${rss}/1024))MB${COLOR_OFF} ${COLOR_CYAN}PSS: $((${pss}/1024))MB${COLOR_OFF} ${COLOR_RED}USS: $(( (${private_clean} + ${private_dirty}) /1024 ))MB${COLOR_OFF}" + sleep $WAIT + count=$(($count + 1)) + done +} -cat << EOF +function get::meminfo_once() { + + get::meminfo + + echo -e "${COLOR_GRAY} # OS meminfo MemTotal:内存总数 MemFree:空闲内存数 @@ -60,21 +107,16 @@ Shared_Dirty: 和其他进程共享的被改写的page的大小 Private_Clean: 未被改写的私有页面的大小。 Private_Dirty: 已被改写的私有页面的大小。 Swap: 存在于交换分区的数据大小(如果物理内存有限,可能存在一部分在主存一部分在交换分区) -SwapPss: 计算逻辑就跟pss一样,只不过针对的是交换分区的内存。 - -Pid: ${pid} -Cmd: $(tr -d '\0' < /proc/${pid}/cmdline | cut -c1-80) -User: $(id -nu < /proc/${pid}/loginuid ) -Threads: $(awk '/Threads:/ {print $2}' /proc/${pid}/status) +SwapPss: 计算逻辑就跟pss一样,只不过针对的是交换分区的内存。${COLOR_OFF} +" + get::pidinfo -File: /proc/${pid}/smaps - -# Os meminfo + echo -e "${COLOR_GREEN}# Os meminfo MemTotal: ${mem_total} KB MemFree: ${mem_free} KB -MemAvailable: ${mem_available} KB +MemAvailable: ${mem_available} KB ${COLOR_OFF} -# Process smaps +${COLOR_CYAN}# Process smaps Size: ${size} KB RSS: ${rss} kB PSS: ${pss} kB @@ -86,4 +128,69 @@ Swap: ${swap} kB SwapPss: ${swap_pss} kB USS: ${private_clean} + ${private_dirty} = $(( ${private_clean} + ${private_dirty} )) kB +${COLOR_OFF} +" +} + +function help::usage { + cat << EOF + +Get Process Memory information. + +Usage: + $(basename $0) [options] + +Options: + -p,--pid Process id + -r,--retries Retries number + -w,--wait Retries wit time + -h,--help View help + --nocolor Do not output color + EOF + exit +} +###################################################################################################### +# main +###################################################################################################### + +#[ "$#" == "0" ] && help::usage + +while [ "${1:-}" != "" ]; do + case $1 in + -p | --pid ) shift + PID=${1:-$PID} + ;; + -r | --retries ) shift + RETRIES=${1:-$RETRIES} + ;; + -w | --wait ) shift + WAIT=${1:-$WAIT} + ;; + -h | --help ) help::usage + ;; + --nocolor ) NOCOLOR=true + ;; + * ) help::usage + exit 1 + esac + shift +done + + +if [ "${NOCOLOR}" == "true" ]; then + COLOR_RED="" + COLOR_GREEN="" + COLOR_YELLOW="" + COLOR_BLUE="" + COLOR_PURPLE="" + COLOR_CYAN="" + COLOR_GRAY="" + COLOR_OFF="" +fi + +if [[ ${RETRIES} -gt 0 ]]; then + get::meminfo_loop +else + get::meminfo_once +fi diff --git a/shell/getgcr.sh b/shell/k8s/getgcr.sh similarity index 100% rename from shell/getgcr.sh rename to shell/k8s/getgcr.sh diff --git a/shell/k8s/k8s-app-info.sh b/shell/k8s/k8s-app-info.sh new file mode 100644 index 0000000..c908d5f --- /dev/null +++ b/shell/k8s/k8s-app-info.sh @@ -0,0 +1,189 @@ +#!/usr/bin/env bash +################################################################### +#Script Name : k8s_app_info.sh +#Description : get app info. +#Create Date : 2020-11-19 +#Author : lework +#Email : lework@yeah.net +################################################################### + + +[[ -n $DEBUG ]] && set -x || true +set -o errtrace # Make sure any error trap is inherited +set -o nounset # Disallow expansion of unset variables +set -o pipefail # Use last non-zero exit code in a pipeline + + +###################################################################################################### +# environment configuration +###################################################################################################### + +NAMESPACE="${NAMESPACE:-default}" +APPNAME="${APPNAME:-}" +SELECTOR="${SELECTOR:-}" +INFO_FILE="k8s-app-info_$(date +%s).md" + +###################################################################################################### +# function +###################################################################################################### + +function log::echo { + local code=$1 + local space=$2 + local text=$3 + [[ "$code" == "0" ]] && code=32 || { code=31; text="ERROR"; } + echo -e "\033[0;${code}m $(head -c $((12-${space})) /dev/zero |tr '\0' '.')........................ ${text}\033[0m" + +} + +function file::write { + printf "%s\n" "$*" >> $INFO_FILE +} + +function exec::kubectl { + local result + local code + + result="$(kubectl -n $NAMESPACE $* 2>/dev/null)" + code="$?" + if [[ "$code" == "0" ]]; then + file::write " +\`\`\`bash +# kubectl -n $NAMESPACE $* +${result} +\`\`\`" + fi + return "$code" +} + + +function get::selector { + echo -ne "Get Selector" + if [[ "${SELECTOR}" == "" ]]; then + selflink=$(kubectl -n $NAMESPACE get deployment $APPNAME -o yaml --ignore-not-found 2>/dev/null | awk '/selfLink:/ {print $2}') + SELECTOR=$(kubectl get --raw "${selflink}/scale" 2>/dev/null | sed 's/.*selector":"\(.*\)".*/\1/g') + fi + + if [[ "${SELECTOR}" == "" ]]; then + echo -e "\033[0;31m[Error] not found $APPNAME selector\033[0m" + exit 1 + fi + file::write " +# [INFO] +namespace: \`${NAMESPACE}\`$(if [[ "$APPNAME" != "" ]];then echo -e "\nname: \`${APPNAME}\`";fi) +selector: \`${SELECTOR}\` +" + log::echo "0" "8" "OK" +} + +function get::describe { + control=$1 + + echo -ne "Get ${control^}" + file::write "# [${control^}]" + names=$(kubectl -n $NAMESPACE get $control -l "$SELECTOR" --no-headers --ignore-not-found 2>/dev/null | awk '{print $1}') + + [[ "$names" == "" && "$APPNAME" != "" ]] && names=$(kubectl -n $NAMESPACE get $control $APPNAME --no-headers --ignore-not-found 2>/dev/null | awk '{print $1}') + + for i in $names; do + file::write "## $i" + exec::kubectl describe $control $i + exec::kubectl get $control $i -o yaml + done + log::echo "$?" "${#control}" "$(echo $names | wc -w)" +} + +function get::pods_log { + echo -ne "Get Pod log" + file::write "# [Pod Log]" + names=$(kubectl -n $NAMESPACE get pods -l "$SELECTOR" --no-headers --ignore-not-found 2>/dev/null | awk '{print $1}' 2>/dev/null) + log::echo "$?" "7" "$(echo $names | wc -w)" + for i in $names; do + echo "Get Pod: $i" + file::write "## $i" + exec::kubectl logs --tail 200 $i --all-containers + done +} + +function get::k8s_event { + echo -ne "Get k8s Event" + file::write "# [Event]" + exec::kubectl get event + log::echo "$?" "9" "OK" +} + +function get::cluster { + echo -ne "Get Cluster" + file::write "# [Cluster]" + exec::kubectl top node + log::echo "$?" "7" "OK" +} + + +function get::info { + get::selector + + get::describe ingress + get::describe service + get::describe endpoints + get::describe deployment + get::describe replicaset + get::describe daemonset + get::describe cronjob + get::describe job + get::describe pod + get::describe configmaps + get::describe secrets + get::pods_log + get::k8s_event + get::cluster + +} + +function help::usage { + # 使用帮助 + + cat << EOF + +Get k8s app info. + +Usage: + $(basename $0) [flag] + +Flag: + -ns,--namespace namespace + -n,--name name + -l,--selector selector +EOF + + exit 1 +} + + ###################################################################################################### +# main +###################################################################################################### + + +[ "$#" == "0" ] && help::usage || true + +while [ "${1:-}" != "" ]; do + case $1 in + -ns | --namespace ) shift + NAMESPACE=${1:-$NAMESPACE} + ;; + -n | --name ) shift + APPNAME=${1:-$APPNAME} + ;; + -l | --selector ) shift + SELECTOR=${1:-$SELECTOR} + ;; + * ) help::usage + esac + shift +done + +[[ "${APPNAME}" == "" && "${SELECTOR}" == "" ]] && help::usage +[ -f "${INFO_FILE}" ] && rm -f "${INFO_FILE}" + +get::info +echo -e "\nFile: ${INFO_FILE}" diff --git a/shell/k8s/k8s-backup.sh b/shell/k8s/k8s-backup.sh new file mode 100644 index 0000000..ed1cc12 --- /dev/null +++ b/shell/k8s/k8s-backup.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +################################################################### +#Script Name : k8s-backup.sh +#Description : backup k8s resources. +#Create Date : 2020-11-19 +#Author : lework +#Email : lework@yeah.net +################################################################### +# https://github.com/pieterlange/kube-backup/blob/master/entrypoint.sh + +resources_path="./backup-$(date +%s)" + +function getall { + ns=$1 + for r in $(kubectl api-resources --verbs=list --namespaced -o name | grep -v "events.events.k8s.io" | grep -v "events" | sort | uniq); do + echo "Resource:" $r + for l in $(kubectl -n ${ns} get --ignore-not-found ${r} -o jsonpath="{$.items[*].metadata.name}");do + kubectl -n ${ns} get --ignore-not-found ${r} ${l} -o yaml \ + | sed -n "/ managedFields:/{p; :a; N; / name: ${l}/!ba; s/.*\\n//}; p" \ + | sed -e 's/ uid:.*//g' \ + -e 's/ resourceVersion:.*//g' \ + -e 's/ selfLink:.*//g' \ + -e 's/ creationTimestamp:.*//g' \ + -e 's/ managedFields:.*//g' \ + -e '/^\s*$/d' > "$resources_path/${n}/${l}.${r}.yaml" + done + done +} + +for n in $(kubectl get ns -o jsonpath="{$.items[*].metadata.name}");do + echo "Namespace:" $n + [ -d "$resources_path/$n" ] || mkdir -p "$resources_path/$n" + kubectl get ns ${n} --ignore-not-found -o yaml \ + | sed -n "/ managedFields:/{p; :a; N; / name: ${n}/!ba; s/.*\\n//}; p" \ + | sed -e 's/ uid:.*//g' \ + -e 's/ resourceVersion:.*//g' \ + -e 's/ selfLink:.*//g' \ + -e 's/ creationTimestamp:.*//g' \ + -e 's/ managedFields:.*//g' \ + -e '/^\s*$/d' > "$resources_path/${n}/namespace.yaml" + getall $n +done + +echo "File: ${resources_path}" diff --git a/shell/kube-logging.sh b/shell/k8s/kube-logging.sh similarity index 100% rename from shell/kube-logging.sh rename to shell/k8s/kube-logging.sh diff --git a/shell/library.sh b/shell/library.sh index 38449cc..728b9ed 100644 --- a/shell/library.sh +++ b/shell/library.sh @@ -794,4 +794,13 @@ command_exists() { fi } - +function utils::quote() { + # 引号 + if [ $(echo "$@" | tr -d "\n" | wc -c) -eq 0 ]; then + echo "''" + elif [ $(echo "$@" | tr -d "[a-z][A-Z][0-9]:,.=~_/\n-" | wc -c) -gt 0 ]; then + echo "$@" | sed -e "s/'/\'\"\'\"\'/g" | sed -e "s/^/'/g" -e "s/$/'/g" + else + echo "$@" + fi +} diff --git a/shell/move_train.sh b/shell/move_train.sh new file mode 100644 index 0000000..4b6998f --- /dev/null +++ b/shell/move_train.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + + +train=""" + _-====-__-____-============-__ + _( _) + OO( Hello, Baby! )_ + 0 (_ _) + o0 (_ _) + o \`=-___-===-_____-========-__) + .o _________ + . ______ ______________ | | _____ + _()_||__|| ________ | | |_________| __||___||__ + ( | | | | | |Y_____00_| |_ _| +/-OO----OO**=*OO--OO*=*OO--------OO*=*OO-------OO*=*OO-------OO*=P +""" + +i=$(( $(stty size | cut -d" " -f2) - 67 )) + +while [ $i -gt 1 ]; do + clear + tput setaf $(( $i % 7 + 1 )) + printf "$train" | pr -tro $i + sleep 0.5 + tput setf 0 + (( i = i - 1 )) +done diff --git a/shell/cfssl.sh b/shell/ssl/cfssl.sh similarity index 100% rename from shell/cfssl.sh rename to shell/ssl/cfssl.sh diff --git a/shell/ssl/gen_ssl_certs.sh b/shell/ssl/gen_ssl_certs.sh new file mode 100644 index 0000000..c18ba25 --- /dev/null +++ b/shell/ssl/gen_ssl_certs.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash + +set -e + +ROOT_DOMAIN=$1 +SYS_DOMAIN=sys.$ROOT_DOMAIN +APPS_DOMAIN=apps.$ROOT_DOMAIN + +DOMAIN_DIR="${ROOT_DOMAIN}_cert" +SSL_FILE=sslconf-${ROOT_DOMAIN}.conf + +[ ! -d "${DOMAIN_DIR}" ] && mkdir "${DOMAIN_DIR}" +cd "${DOMAIN_DIR}" + +#Generate SSL Config with SANs +if [ ! -f $SSL_FILE ]; then + cat > $SSL_FILE < ${ROOT_DOMAIN}_fullchain.pem +openssl dhparam -out dhparam.pem 2048 + +rm ${ROOT_DOMAIN}.csr diff --git a/shell/keystore.sh b/shell/ssl/keystore.sh similarity index 100% rename from shell/keystore.sh rename to shell/ssl/keystore.sh diff --git a/shell/util.sh b/shell/util.sh new file mode 100644 index 0000000..90bb07b --- /dev/null +++ b/shell/util.sh @@ -0,0 +1,2684 @@ +#!/bin/bash -e + +################### +# ARRAY UTILITIES # +################### + +function arrayToParameters() +{ + local -r array=("${@}") + + local -r string="$(printf "'%s' " "${array[@]}")" + + echo "${string:0:${#string} - 1}" +} + +function arrayToString() +{ + local -r array=("${@}") + + arrayToStringWithDelimiter ',' "${array[@]}" +} + +function arrayToStringWithDelimiter() +{ + local -r delimiter="${1}" + local -r list=("${@:2}") + + local -r string="$(printf "%s${delimiter}" "${list[@]}")" + + echo "${string:0:${#string} - ${#delimiter}}" +} + +function excludeElementFromArray() +{ + local -r element="${1}" + local array=("${@:2}") + + local i=0 + + for ((i = 0; i < ${#array[@]}; i = i + 1)) + do + if [[ "${array[i]}" = "${element}" ]] + then + unset array['${i}'] + fi + done + + echo "${array[@]}" +} + +function isElementInArray() +{ + local -r element="${1}" + local -r array=("${@:2}") + + local walker='' + + for walker in "${array[@]}" + do + [[ "${walker}" = "${element}" ]] && echo 'true' && return 0 + done + + echo 'false' && return 1 +} + +function sortUniqArray() +{ + local -r array=("${@}") + + trimString "$(tr ' ' '\n' <<< "${array[@]}" | sort -u | tr '\n' ' ')" +} + +##################### +# COMPILE UTILITIES # +##################### + +function compileAndInstallFromSource() +{ + local -r downloadURL="${1}" + local -r installFolderPath="${2}" + local -r installFileOrFolderBinPath="${3}" + local -r user="${4}" + + initializeFolder "${installFolderPath}" + + local -r currentWorkingDirectory="$(pwd)" + local -r tempFolder="$(getTemporaryFolder)" + + unzipRemoteFile "${downloadURL}" "${tempFolder}" + cd "${tempFolder}" + "${tempFolder}/configure" --prefix="${installFolderPath}" + make + make install + chown -R "${user}:$(getUserGroupName "${user}")" "${installFolderPath}" + symlinkUsrBin "${installFileOrFolderBinPath}" + cd "${currentWorkingDirectory}" + rm -f -r "${tempFolder}" +} + +####################### +# DATE TIME UTILITIES # +####################### + +function convertISO8601ToSeconds() +{ + local -r time="${1}" + + if [[ "$(isMacOperatingSystem)" = 'true' ]] + then + date -j -u -f '%FT%T' "$(awk -F '.' '{ print $1 }' <<< "${time}" | tr -d 'Z')" +'%s' + elif [[ "$(isAmazonLinuxDistributor)" = 'true' || "$(isCentOSDistributor)" = 'true' || "$(isRedHatDistributor)" = 'true' || "$(isUbuntuDistributor)" = 'true' ]] + then + date -d "${time}" +'%s' + else + fatal '\nFATAL : only support Amazon-Linux, CentOS, Mac, RedHat, or Ubuntu OS' + fi +} + +function getISO8601DateTimeNow() +{ + date -u +'%Y-%m-%dT%H:%M:%SZ' +} + +function getUTCNowInSeconds() +{ + date -u +'%s' +} + +function secondsToReadableTime() +{ + local -r time="${1}" + + local -r day="$((time / 60 / 60 / 24))" + local -r hour="$((time / 60 / 60 % 24))" + local -r minute="$((time / 60 % 60))" + local -r second="$((time % 60))" + + if [[ "${day}" = '0' ]] + then + printf '%02d:%02d:%02d' "${hour}" "${minute}" "${second}" + elif [[ "${day}" = '1' ]] + then + printf '%d day and %02d:%02d:%02d' "${day}" "${hour}" "${minute}" "${second}" + else + printf '%d days and %02d:%02d:%02d' "${day}" "${hour}" "${minute}" "${second}" + fi +} + +######################## +# FILE LOCAL UTILITIES # +######################## + +function appendToFileIfNotFound() +{ + local -r file="${1}" + local -r pattern="${2}" + local -r string="${3}" + local -r patternAsRegex="${4}" + local -r stringAsRegex="${5}" + local -r addNewLine="${6}" + + # Validate Inputs + + checkExistFile "${file}" + checkNonEmptyString "${pattern}" 'undefined pattern' + checkNonEmptyString "${string}" 'undefined string' + checkTrueFalseString "${patternAsRegex}" + checkTrueFalseString "${stringAsRegex}" + + if [[ "${stringAsRegex}" = 'false' ]] + then + checkTrueFalseString "${addNewLine}" + fi + + # Append String + + if [[ "${patternAsRegex}" = 'true' ]] + then + local -r found="$(grep -E -o "${pattern}" "${file}")" + else + local -r found="$(grep -F -o "${pattern}" "${file}")" + fi + + if [[ "$(isEmptyString "${found}")" = 'true' ]] + then + if [[ "${stringAsRegex}" = 'true' ]] + then + echo -e "${string}" >> "${file}" + else + if [[ "${addNewLine}" = 'true' ]] + then + echo >> "${file}" + fi + + echo "${string}" >> "${file}" + fi + fi +} + +function checkExistFile() +{ + local -r file="${1}" + local -r errorMessage="${2}" + + if [[ "${file}" = '' || ! -f "${file}" ]] + then + if [[ "$(isEmptyString "${errorMessage}")" = 'true' ]] + then + fatal "\nFATAL : file '${file}' not found" + fi + + fatal "\nFATAL : ${errorMessage}" + fi +} + +function checkExistFolder() +{ + local -r folder="${1}" + local -r errorMessage="${2}" + + if [[ "${folder}" = '' || ! -d "${folder}" ]] + then + if [[ "$(isEmptyString "${errorMessage}")" = 'true' ]] + then + fatal "\nFATAL : folder '${folder}' not found" + fi + + fatal "\nFATAL : ${errorMessage}" + fi +} + +function checkValidJSONContent() +{ + local -r content="${1}" + + if [[ "$(isValidJSONContent "${content}")" = 'false' ]] + then + fatal '\nFATAL : invalid JSON' + fi +} + +function checkValidJSONFile() +{ + local -r file="${1}" + + if [[ "$(isValidJSONFile "${file}")" = 'false' ]] + then + fatal "\nFATAL : invalid JSON file '${file}'" + fi +} + +function cleanUpSystemFolders() +{ + header 'CLEANING UP SYSTEM FOLDERS' + + local -r folders=( + '/tmp' + '/var/tmp' + ) + + local folder='' + + for folder in "${folders[@]}" + do + echo "Cleaning up folder '${folder}'" + emptyFolder "${folder}" + done +} + +function copyFolderContent() +{ + local -r sourceFolder="${1}" + local -r destinationFolder="${2}" + + checkExistFolder "${sourceFolder}" + checkExistFolder "${destinationFolder}" + + find "${sourceFolder}" \ + -mindepth 1 \ + -maxdepth 1 \ + -exec cp -p -r '{}' "${destinationFolder}" \; +} + +function createAbsoluteUsrBin() +{ + local -r binFileName="${1}" + local -r sourceFilePath="${2}" + + checkExistFile "${sourceFilePath}" + + mkdir -p '/usr/bin' + printf "#!/bin/bash -e\n\n'%s' \"\${@}\"" "${sourceFilePath}" > "/usr/bin/${binFileName}" + chmod 755 "/usr/bin/${binFileName}" +} + +function createFileFromTemplate() +{ + local -r sourceFile="${1}" + local -r destinationFile="${2}" + local -r oldNewData=("${@:3}") + + checkExistFile "${sourceFile}" + checkExistFolder "$(dirname "${destinationFile}")" + + local content='' + content="$(cat "${sourceFile}")" + + local i=0 + + for ((i = 0; i < ${#oldNewData[@]}; i = i + 2)) + do + content="$(replaceString "${content}" "${oldNewData[${i}]}" "${oldNewData[${i} + 1]}")" + done + + echo "${content}" > "${destinationFile}" +} + +function createInitFileFromTemplate() +{ + local -r serviceName="${1}" + local -r templateFolderPath="${2}" + local -r initConfigDataFromTemplate=("${@:3}") + + createFileFromTemplate \ + "${templateFolderPath}/${serviceName}.service.systemd" \ + "/etc/systemd/system/${serviceName}.service" \ + "${initConfigDataFromTemplate[@]}" +} + +function deleteOldLogs() +{ + local logFolderPaths=("${@}") + + header 'DELETING OLD LOGS' + + # Default Log Folder Path + + if [[ "${#logFolderPaths[@]}" -lt '1' ]] + then + logFolderPaths+=('/var/log') + fi + + # Walk Each Log Folder Path + + local i=0 + + for ((i = 0; i < ${#logFolderPaths[@]}; i = i + 1)) + do + checkExistFolder "${logFolderPaths[i]}" + + find \ + -L \ + "${logFolderPaths[i]}" \ + -type f \ + \( \ + -regex '.*-[0-9]+' -o \ + -regex '.*\.[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]\.log' -o \ + -regex '.*\.[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]\.txt' -o \ + -regex '.*\.[0-9]+' -o \ + -regex '.*\.[0-9]+\.log' -o \ + -regex '.*\.gz' -o \ + -regex '.*\.log\.[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]T[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' -o \ + -regex '.*\.old' -o \ + -regex '.*\.xz' \ + \) \ + -delete \ + -print + done +} + +function emptyFolder() +{ + local -r folder="${1}" + + checkExistFolder "${folder}" + + find "${folder}" \ + -mindepth 1 \ + -delete +} + +function getFileExtension() +{ + local -r string="${1}" + + local -r fullFileName="$(basename "${string}")" + + echo "${fullFileName##*.}" +} + +function getFileName() +{ + local -r string="${1}" + + local -r fullFileName="$(basename "${string}")" + + echo "${fullFileName%.*}" +} + +function getTemporaryFile() +{ + local extension="${1}" + + if [[ "$(isEmptyString "${extension}")" = 'false' && "$(grep -i -o "^." <<< "${extension}")" != '.' ]] + then + extension=".${extension}" + fi + + mktemp "$(getTemporaryFolderRoot)/$(date +'%Y%m%d-%H%M%S')-XXXXXXXXXX${extension}" +} + +function getTemporaryFolder() +{ + mktemp -d "$(getTemporaryFolderRoot)/$(date +'%Y%m%d-%H%M%S')-XXXXXXXXXX" +} + +function getTemporaryFolderRoot() +{ + local temporaryFolder='/tmp' + + if [[ "$(isEmptyString "${TMPDIR}")" = 'false' ]] + then + temporaryFolder="$(formatPath "${TMPDIR}")" + fi + + echo "${temporaryFolder}" +} + +function initializeFolder() +{ + local -r folder="${1}" + + if [[ -d "${folder}" ]] + then + emptyFolder "${folder}" + else + mkdir -p "${folder}" + fi +} + +function isValidJSONContent() +{ + local -r content="${1}" + + if ( python -m 'json.tool' <<< "${content}" &> '/dev/null' ) + then + echo 'true' && return 0 + fi + + echo 'false' && return 1 +} + +function isValidJSONFile() +{ + local -r file="${1}" + + checkExistFile "${file}" + + isValidJSONContent "$(cat "${file}")" +} + +function moveFolderContent() +{ + local -r sourceFolder="${1}" + local -r destinationFolder="${2}" + + checkExistFolder "${sourceFolder}" + checkExistFolder "${destinationFolder}" + + find "${sourceFolder}" \ + -mindepth 1 \ + -maxdepth 1 \ + -exec mv '{}' "${destinationFolder}" \; +} + +function redirectOutputToLogFile() +{ + local -r logFile="${1}" + + mkdir -p "$(dirname "${logFile}")" + exec > >(tee -a "${logFile}") 2>&1 +} + +function resetFolderPermission() +{ + local -r folderPath="${1}" + local -r userLogin="${2}" + local -r groupName="${3}" + + checkExistFolder "${folderPath}" + checkExistUserLogin "${userLogin}" + checkExistGroupName "${groupName}" + + header "RESETTING FOLDER PERMISSION ${folderPath}" + + chown -R "${userLogin}:${groupName}" "${folderPath}" + + find "${folderPath}" \ + -type d \ + \( \ + -not -path "*/.git" -a \ + -not -path "*/.git/*" \ + \) \ + -exec chmod 700 '{}' \; \ + -print + + find "${folderPath}" \ + -type f \ + \( \ + -not -path "*/.git" -a \ + -not -path "*/.git/*" \ + \) \ + -exec chmod 600 '{}' \; \ + -print +} + +function resetLogs() +{ + local logFolderPaths=("${@}") + + # Default Log Folder Path + + if [[ "${#logFolderPaths[@]}" -lt '1' ]] + then + logFolderPaths+=('/var/log') + fi + + # Delete Old Logs + + deleteOldLogs "${logFolderPaths[@]}" + + # Reset Logs + + header 'RESETTING LOGS' + + local i=0 + + for ((i = 0; i < ${#logFolderPaths[@]}; i = i + 1)) + do + checkExistFolder "${logFolderPaths[i]}" + + find "${logFolderPaths[i]}" \ + -type f \ + -exec cp -f '/dev/null' '{}' \; \ + -print + done +} + +function symlinkListUsrBin() +{ + local -r sourceFilePaths=("${@}") + + local sourceFilePath='' + + for sourceFilePath in "${sourceFilePaths[@]}" + do + chmod 755 "${sourceFilePath}" + rm -f -r "/usr/bin/$(basename "${sourceFilePath}")" + ln -f -s "${sourceFilePath}" "/usr/bin/$(basename "${sourceFilePath}")" + done +} + +function symlinkUsrBin() +{ + local -r sourceBinFileOrFolder="${1}" + + if [[ "$(isMacOperatingSystem)" = 'true' ]] + then + mkdir -p '/usr/bin' + + if [[ -d "${sourceBinFileOrFolder}" ]] + then + find "${sourceBinFileOrFolder}" -maxdepth 1 \( -type f -o -type l \) -perm -u+x -exec bash -c -e ' + for file + do + fileType="$(stat -f "%HT" "${file}")" + + if [[ "${fileType}" = "Regular File" ]] + then + ln -f -s "${file}" "/usr/bin/$(basename "${file}")" + elif [[ "${fileType}" = "Symbolic Link" ]] + then + cd "$(dirname "${file}")" + + if [[ -f "$(readlink "${file}")" ]] + then + ln -f -s "${file}" "/usr/bin/$(basename "${file}")" + fi + fi + done' bash '{}' \; + elif [[ -f "${sourceBinFileOrFolder}" ]] + then + ln -f -s "${sourceBinFileOrFolder}" "/usr/bin/$(basename "${sourceBinFileOrFolder}")" + else + fatal "\nFATAL : '${sourceBinFileOrFolder}' is not directory or file" + fi + elif [[ "$(isAmazonLinuxDistributor)" = 'true' || "$(isCentOSDistributor)" = 'true' || "$(isRedHatDistributor)" = 'true' || "$(isUbuntuDistributor)" = 'true' ]] + then + mkdir -p '/usr/bin' + + if [[ -d "${sourceBinFileOrFolder}" ]] + then + find "${sourceBinFileOrFolder}" -maxdepth 1 -xtype f -perm -u+x -exec bash -c -e ' + for file + do + ln -f -s "${file}" "/usr/bin/$(basename "${file}")" + done' bash '{}' \; + elif [[ -f "${sourceBinFileOrFolder}" ]] + then + ln -f -s "${sourceBinFileOrFolder}" "/usr/bin/$(basename "${sourceBinFileOrFolder}")" + else + fatal "\nFATAL : '${sourceBinFileOrFolder}' is not directory or file" + fi + else + fatal '\nFATAL : only support Amazon-Linux, CentOS, Mac, RedHat, or Ubuntu OS' + fi +} + +function trimFile() +{ + local -r filePath="${1}" + + checkExistFile "${filePath}" + + printf '%s' "$(< "${filePath}")" > "${filePath}" +} + +######################### +# FILE REMOTE UTILITIES # +######################### + +function checkExistURL() +{ + local -r url="${1}" + + if [[ "$(existURL "${url}")" = 'false' ]] + then + fatal "\nFATAL : url '${url}' not found" + fi +} + +function downloadFile() +{ + local -r url="${1}" + local -r destinationFile="${2}" + local overwrite="${3}" + + checkExistURL "${url}" + + # Check Overwrite + + if [[ "$(isEmptyString "${overwrite}")" = 'true' ]] + then + overwrite='false' + fi + + checkTrueFalseString "${overwrite}" + + # Validate + + if [[ -f "${destinationFile}" ]] + then + if [[ "${overwrite}" = 'false' ]] + then + fatal "\nFATAL : file '${destinationFile}' found" + fi + + rm -f "${destinationFile}" + elif [[ -e "${destinationFile}" ]] + then + fatal "\nFATAL : file '${destinationFile}' already exists" + fi + + # Download + + debug "\nDownloading '${url}' to '${destinationFile}'\n" + curl -L "${url}" -o "${destinationFile}" --retry 12 --retry-delay 5 +} + +function existURL() +{ + local -r url="${1}" + + # Install Curl + + installCURLCommand > '/dev/null' + + # Check URL + + if ( curl -f --head -L "${url}" -o '/dev/null' -s --retry 12 --retry-delay 5 || + curl -f -L "${url}" -o '/dev/null' -r 0-0 -s --retry 12 --retry-delay 5 ) + then + echo 'true' && return 0 + fi + + echo 'false' && return 1 +} + +function getRemoteFileContent() +{ + local -r url="${1}" + + checkExistURL "${url}" + curl -s -X 'GET' -L "${url}" --retry 12 --retry-delay 5 +} + +function unzipRemoteFile() +{ + local -r downloadURL="${1}" + local -r installFolder="${2}" + local extension="${3}" + + # Install Curl + + installCURLCommand + + # Validate URL + + checkExistURL "${downloadURL}" + + # Find Extension + + local exExtension='' + + if [[ "$(isEmptyString "${extension}")" = 'true' ]] + then + extension="$(getFileExtension "${downloadURL}")" + exExtension="$(rev <<< "${downloadURL}" | cut -d '.' -f 1-2 | rev)" + fi + + # Unzip + + if [[ "$(grep -i '^tgz$' <<< "${extension}")" != '' || "$(grep -i '^tar\.gz$' <<< "${extension}")" != '' || "$(grep -i '^tar\.gz$' <<< "${exExtension}")" != '' ]] + then + debug "\nDownloading '${downloadURL}'\n" + curl -L "${downloadURL}" --retry 12 --retry-delay 5 | tar -C "${installFolder}" -x -z --strip 1 + echo + elif [[ "$(grep -i '^tar\.bz2$' <<< "${exExtension}")" != '' ]] + then + # Install BZip2 + + installBZip2Command + + # Unzip + + debug "\nDownloading '${downloadURL}'\n" + curl -L "${downloadURL}" --retry 12 --retry-delay 5 | tar -C "${installFolder}" -j -x --strip 1 + echo + elif [[ "$(grep -i '^zip$' <<< "${extension}")" != '' ]] + then + # Install Unzip + + installUnzipCommand + + # Unzip + + if [[ "$(existCommand 'unzip')" = 'false' ]] + then + fatal 'FATAL : command unzip not found' + fi + + local -r zipFile="${installFolder}/$(basename "${downloadURL}")" + + downloadFile "${downloadURL}" "${zipFile}" 'true' + unzip -q "${zipFile}" -d "${installFolder}" + rm -f "${zipFile}" + echo + else + fatal "\nFATAL : file extension '${extension}' not supported" + fi +} + +################# +# GIT UTILITIES # +################# + +function getGitPrivateRepositorySSHURL() +{ + local -r user="${1}" + local -r token="${2}" + local -r orgName="${3}" + local -r gitURL="${4}" + + getGitUserRepositoryObjectKey "${user}" "${token}" 'ssh_url' 'private' "${orgName}" "${gitURL}" +} + +function getGitPublicRepositorySSHURL() +{ + local -r user="${1}" + local -r token="${2}" + local -r orgName="${3}" + local -r gitURL="${4}" + + getGitUserRepositoryObjectKey "${user}" "${token}" 'ssh_url' 'public' "${orgName}" "${gitURL}" +} + +function getGitRepositoryNameFromCloneURL() +{ + local -r cloneURL="${1}" + + checkNonEmptyString "${cloneURL}" 'undefined clone url' + + if [[ "$(grep -F -o '@' <<< "${cloneURL}")" != '' ]] + then + awk -F '/' '{ print $2 }' <<< "${cloneURL}" | rev | cut -d '.' -f 2- | rev + else + awk -F '/' '{ print $5 }' <<< "${cloneURL}" | rev | cut -d '.' -f 2- | rev + fi +} + +function getGitUserName() +{ + local -r user="${1}" + local -r token="${2}" + local gitURL="${3}" + + # Default Value + + if [[ "$(isEmptyString "${gitURL}")" = 'true' ]] + then + gitURL='https://api.github.com' + fi + + # Validation + + checkNonEmptyString "${user}" 'undefined user' + checkNonEmptyString "${token}" 'undefined token' + + # Get User Name + + curl \ + -s \ + -X 'GET' \ + -u "${user}:${token}" \ + -L "${gitURL}/user" \ + --retry 12 \ + --retry-delay 5 | + jq \ + --compact-output \ + --raw-output \ + --sort-keys \ + '.["name"] // empty' +} + +function getGitUserPrimaryEmail() +{ + local -r user="${1}" + local -r token="${2}" + local gitURL="${3}" + + # Default Values + + if [[ "$(isEmptyString "${gitURL}")" = 'true' ]] + then + gitURL='https://api.github.com' + fi + + # Validation + + checkNonEmptyString "${user}" 'undefined user' + checkNonEmptyString "${token}" 'undefined token' + + # Pagination + + local page=1 + local exitCount=0 + + for ((page = 1; page > exitCount; page = page + 1)) + do + local emails='' + emails="$( + curl \ + -s \ + -X 'GET' \ + -u "${user}:${token}" \ + -L "${gitURL}/user/emails?page=${page}&per_page=100" \ + --retry 12 \ + --retry-delay 5 | + jq \ + --compact-output \ + --raw-output \ + --sort-keys \ + '.[] // empty' \ + )" + + local primaryEmail='' + primaryEmail="$( + jq \ + --compact-output \ + --raw-output \ + --sort-keys \ + 'select(.["primary"] == true) | + .["email"] // empty' \ + <<< "${emails}" + )" + + if [[ "$(isEmptyString "${primaryEmail}")" = 'false' || "$(isEmptyString "${emails}")" = 'true' ]] + then + echo "${primaryEmail}" + exitCount="$((page + 1))" + fi + done +} + +function getGitUserRepositoryObjectKey() +{ + local -r user="${1}" + local -r token="${2}" + local -r objectKey="${3}" + local -r kind="${4}" + local -r orgName="${5}" + local gitURL="${6}" + + # Default Value + + if [[ "$(isEmptyString "${gitURL}")" = 'true' ]] + then + gitURL='https://api.github.com' + fi + + # Validation + + checkNonEmptyString "${user}" 'undefined user' + checkNonEmptyString "${token}" 'undefined token' + checkNonEmptyString "${objectKey}" 'undefined object key' + checkNonEmptyString "${gitURL}" 'undefined git url' + + # Pagination + + local results='' + local page=1 + local exitCount=0 + + for ((page = 1; page > exitCount; page = page + 1)) + do + # User or Organization + + if [[ "$(isEmptyString "${orgName}")" = 'true' ]] + then + local targetURL="${gitURL}/user/repos?affiliation=owner&page=${page}&per_page=100&visibility=${kind}" + else + local targetURL="${gitURL}/orgs/${orgName}/repos?page=${page}&per_page=100&type=${kind}" + fi + + # Retrieve Objects + + local currentObjectValue='' + currentObjectValue="$( + curl \ + -s \ + -X 'GET' \ + -u "${user}:${token}" \ + -L "${targetURL}" \ + --retry 12 \ + --retry-delay 5 | + jq \ + --arg jqObjectKey "${objectKey}" \ + --compact-output \ + --raw-output \ + --sort-keys \ + '.[] | + .[$jqObjectKey] // empty' + )" + + if [[ "$(isEmptyString "${currentObjectValue}")" = 'true' ]] + then + exitCount="$((page + 1))" + elif [[ "${page}" = '1' ]] + then + results="$(printf '%s' "${currentObjectValue}")" + else + results="$(printf '%s\n%s' "${results}" "${currentObjectValue}")" + fi + done + + # Return Results + + echo "${results}" | sort -f +} + +##################### +# INSTALL UTILITIES # +##################### + +function installPortableBinary() +{ + local -r appTitleName="${1}" + local -r downloadURL="${2}" + local -r installFolderPath="${3}" + local -r binarySubPaths=($(sortUniqArray "$(replaceString "${4}" ',' ' ')")) + local -r versionOption="${5}" + local -r remoteUnzip="${6}" + + checkNonEmptyString "${appTitleName}" 'undefined app title name' + checkNonEmptyString "${versionOption}" 'undefined version option' + checkTrueFalseString "${remoteUnzip}" + + if [[ "${#binarySubPaths[@]}" -lt '1' ]] + then + fatal '\nFATAL : undefined binary sub paths' + fi + + header "INSTALLING ${appTitleName}" + + checkRequireLinuxSystem + checkRequireRootUser + + umask '0022' + + initializeFolder "${installFolderPath}" + + if [[ "${remoteUnzip}" = 'true' ]] + then + if [[ "$(getFileExtension "${downloadURL}")" = 'sh' ]] + then + curl -s -L "${downloadURL}" --retry 12 --retry-delay 5 | bash -e + else + unzipRemoteFile "${downloadURL}" "${installFolderPath}" + fi + + printf '%s\n\nexport PATH="%s/%s:${PATH}"' \ + '#!/bin/sh -e' \ + "${installFolderPath}" \ + "$(dirname "${binarySubPaths[0]}")" \ + > "/etc/profile.d/$(basename "${installFolderPath}").sh" + + chmod 644 "/etc/profile.d/$(basename "${installFolderPath}").sh" + else + downloadFile "${downloadURL}" "${installFolderPath}/${binarySubPaths[0]}" 'true' + fi + + chown -R "$(whoami):$(whoami)" "${installFolderPath}" + + local binarySubPath='' + + for binarySubPath in "${binarySubPaths[@]}" + do + symlinkListUsrBin "${installFolderPath}/${binarySubPath}" + done + + displayVersion "$("/usr/bin/$(basename "${binarySubPaths[0]}")" "${versionOption}")" + + umask '0077' + + installCleanUp +} + +################# +# MAC UTILITIES # +################# + +function clearMacAppExtendedAttributes() +{ + local -r headerMessage="${1}" + local -r applicationPaths=("${@:2}") + + checkRequireMacSystem + + if [[ "${#applicationPaths[@]}" -gt '0' ]] + then + header "${headerMessage}" + fi + + local applicationPath='' + + for applicationPath in "${applicationPaths[@]}" + do + # Find Non-Default Apple App + + if [[ "$(ls -d -l -O "${applicationPath}" | grep -E '\s+restricted\s+')" = '' ]] + then + info "clearing extended attributes of '${applicationPath}'" + xattr -c -r -s "${applicationPath}" + fi + done +} + +function closeMacApplications() +{ + local -r headerMessage="${1}" + local -r applicationNames=("${@:2}") + + checkRequireMacSystem + + if [[ "${#applicationNames[@]}" -gt '0' ]] + then + header "${headerMessage}" + fi + + local applicationName='' + + for applicationName in "${applicationNames[@]}" + do + applicationName="$(getFileName "${applicationName}")" + + if [[ "${applicationName}" != 'Terminal' ]] + then + local errorMessage='' + errorMessage="$(osascript -e "tell application \"${applicationName}\" to quit" 2>&1)" + + if [[ "$(isEmptyString "${errorMessage}")" = 'true' || "$(grep -E -o '\(-128)$' <<< "${errorMessage}")" != '' ]] + then + info "closing '${applicationName}'" + else + error "${errorMessage}" + fi + fi + done +} + +function getMacCurrentUserICloudDriveFolderPath() +{ + local -r iCloudFolderPath="$(getCurrentUserHomeFolder)/Library/Mobile Documents/com~apple~CloudDocs" + + if [[ -d "${iCloudFolderPath}" ]] + then + echo "${iCloudFolderPath}" + else + echo + fi +} + +function openMacApplications() +{ + local -r headerMessage="${1}" + local -r applicationNames=("${@:2}") + + checkRequireMacSystem + + if [[ "${#applicationNames[@]}" -gt '0' ]] + then + header "${headerMessage}" + fi + + local applicationName='' + + for applicationName in "${applicationNames[@]}" + do + info "openning '${applicationName}'" + osascript -e "tell application \"${applicationName}\" to activate" + done +} + +#################### +# NUMBER UTILITIES # +#################### + +function checkNaturalNumber() +{ + local -r string="${1}" + local -r errorMessage="${2}" + + if [[ "$(isNaturalNumber "${string}")" = 'false' ]] + then + if [[ "$(isEmptyString "${errorMessage}")" = 'true' ]] + then + fatal '\nFATAL : not natural number detected' + fi + + fatal "\nFATAL : ${errorMessage}" + fi +} + +function checkPositiveInteger() +{ + local -r string="${1}" + local -r errorMessage="${2}" + + if [[ "$(isPositiveInteger "${string}")" = 'false' ]] + then + if [[ "$(isEmptyString "${errorMessage}")" = 'true' ]] + then + fatal '\nFATAL : not positive number detected' + fi + + fatal "\nFATAL : ${errorMessage}" + fi +} + +function isNaturalNumber() +{ + local -r string="${1}" + + if [[ "${string}" =~ ^[0-9]+$ ]] + then + echo 'true' && return 0 + fi + + echo 'false' && return 1 +} + +function isPositiveInteger() +{ + local -r string="${1}" + + if [[ "${string}" =~ ^[1-9][0-9]*$ ]] + then + echo 'true' && return 0 + fi + + echo 'false' && return 1 +} + +################ +# OS UTILITIES # +################ + +function checkRequireLinuxSystem() +{ + if [[ "$(isAmazonLinuxDistributor)" = 'false' && "$(isCentOSDistributor)" = 'false' && "$(isRedHatDistributor)" = 'false' && "$(isUbuntuDistributor)" = 'false' ]] + then + fatal '\nFATAL : only support Amazon-Linux, CentOS, RedHat, or Ubuntu OS' + fi + + if [[ "$(is64BitSystem)" = 'false' ]] + then + fatal '\nFATAL : non x86_64 OS found' + fi +} + +function checkRequireMacSystem() +{ + if [[ "$(isMacOperatingSystem)" = 'false' ]] + then + fatal '\nFATAL : only support Mac OS' + fi + + if [[ "$(is64BitSystem)" = 'false' ]] + then + fatal '\nFATAL : non x86_64 OS found' + fi +} + +function getMachineDescription() +{ + lsb_release -d -s +} + +function getMachineRelease() +{ + lsb_release -r -s +} + +function is64BitSystem() +{ + isMachineHardware 'x86_64' +} + +function isAmazonLinuxDistributor() +{ + isDistributor 'amzn' +} + +function isCentOSDistributor() +{ + isDistributor 'centos' +} + +function isDistributor() +{ + local -r distributor="${1}" + + local -r found="$(grep -F -i -o -s "${distributor}" '/proc/version')" + + if [[ "$(isEmptyString "${found}")" = 'true' ]] + then + echo 'false' && return 1 + fi + + echo 'true' && return 0 +} + +function isLinuxOperatingSystem() +{ + isOperatingSystem 'Linux' +} + +function isMachineHardware() +{ + local -r machineHardware="$(escapeGrepSearchPattern "${1}")" + + local -r found="$(uname -m | grep -E -i -o "^${machineHardware}$")" + + if [[ "$(isEmptyString "${found}")" = 'true' ]] + then + echo 'false' && return 1 + fi + + echo 'true' && return 0 +} + +function isMacOperatingSystem() +{ + isOperatingSystem 'Darwin' +} + +function isOperatingSystem() +{ + local -r operatingSystem="$(escapeGrepSearchPattern "${1}")" + + local -r found="$(uname -s | grep -E -i -o "^${operatingSystem}$")" + + if [[ "$(isEmptyString "${found}")" = 'true' ]] + then + echo 'false' && return 1 + fi + + echo 'true' && return 0 +} + +function isRedHatDistributor() +{ + isDistributor 'redhat' +} + +function isUbuntuDistributor() +{ + isDistributor 'ubuntu' +} + +##################### +# PACKAGE UTILITIES # +##################### + +function getLastAptGetUpdate() +{ + if [[ "$(isUbuntuDistributor)" = 'true' ]] + then + local -r aptDate="$(stat -c %Y '/var/cache/apt')" + local -r nowDate="$(date +'%s')" + + echo $((nowDate - aptDate)) + fi +} + +function installBuildEssential() +{ + if [[ "$(isUbuntuDistributor)" = 'true' ]] + then + installPackages 'build-essential' + elif [[ "$(isAmazonLinuxDistributor)" = 'true' || "$(isCentOSDistributor)" = 'true' || "$(isRedHatDistributor)" = 'true' ]] + then + installPackages 'gcc-c++' 'kernel-devel' 'make' + else + fatal '\nFATAL : only support Amazon-Linux, CentOS, RedHat, or Ubuntu OS' + fi +} + +function installBZip2Command() +{ + local -r commandPackage=('bzip2' 'bzip2') + + installCommands "${commandPackage[@]}" +} + +function installCleanUp() +{ + header 'CLEANING UP INSTALLATION' + + if [[ "$(isUbuntuDistributor)" = 'true' ]] + then + DEBIAN_FRONTEND='noninteractive' apt-get --fix-missing -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' autoremove + DEBIAN_FRONTEND='noninteractive' apt-get --fix-missing -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' clean + DEBIAN_FRONTEND='noninteractive' apt-get --fix-missing -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' autoclean + elif [[ "$(isAmazonLinuxDistributor)" = 'true' || "$(isCentOSDistributor)" = 'true' || "$(isRedHatDistributor)" = 'true' ]] + then + yum clean all + else + fatal '\nFATAL : only support Amazon-Linux, CentOS, RedHat, or Ubuntu OS' + fi +} + +function installCommands() +{ + local -r commandPackageData=("${@}") + + if [[ "$(isUbuntuDistributor)" = 'true' ]] + then + runAptGetUpdate '' + fi + + local i=0 + + for ((i = 0; i < ${#commandPackageData[@]}; i = i + 2)) + do + local command="${commandPackageData[${i}]}" + local package="${commandPackageData[${i} + 1]}" + + checkNonEmptyString "${command}" 'undefined command' + checkNonEmptyString "${package}" 'undefined package' + + if [[ "$(existCommand "${command}")" = 'false' ]] + then + installPackages "${package}" + fi + done +} + +function installCURLCommand() +{ + local -r commandPackage=('curl' 'curl') + + installCommands "${commandPackage[@]}" +} + +function installPackage() +{ + local -r aptPackage="${1}" + local -r rpmPackage="${2}" + + if [[ "$(isUbuntuDistributor)" = 'true' ]] + then + if [[ "$(isEmptyString "${aptPackage}")" = 'false' ]] + then + if [[ "$(isAptGetPackageInstall "${aptPackage}")" = 'true' ]] + then + debug "\nApt-Get Package '${aptPackage}' has already been installed" + else + echo -e "\033[1;35m\nInstalling Apt-Get Package '${aptPackage}'\033[0m" + DEBIAN_FRONTEND='noninteractive' apt-get install "${aptPackage}" --fix-missing -y || + (DEBIAN_FRONTEND='noninteractive' apt-get install --fix-missing --yes -f -y && DEBIAN_FRONTEND='noninteractive' apt-get install "${aptPackage}" --fix-missing -y) + fi + fi + elif [[ "$(isAmazonLinuxDistributor)" = 'true' || "$(isCentOSDistributor)" = 'true' || "$(isRedHatDistributor)" = 'true' ]] + then + if [[ "$(isEmptyString "${rpmPackage}")" = 'false' ]] + then + yum install -y "${rpmPackage}" + fi + else + fatal '\nFATAL : only support Amazon-Linux, CentOS, RedHat, or Ubuntu OS' + fi +} + +function installPackages() +{ + local -r packages=("${@}") + + if [[ "$(isUbuntuDistributor)" = 'true' ]] + then + runAptGetUpdate '' + fi + + local package='' + + for package in "${packages[@]}" + do + if [[ "$(isUbuntuDistributor)" = 'true' ]] + then + installPackage "${package}" + elif [[ "$(isAmazonLinuxDistributor)" = 'true' || "$(isCentOSDistributor)" = 'true' || "$(isRedHatDistributor)" = 'true' ]] + then + installPackage '' "${package}" + else + fatal '\nFATAL : only support Amazon-Linux, CentOS, RedHat, or Ubuntu OS' + fi + done +} + +function installPIPCommand() +{ + local -r commandPackage=('pip' 'python-pip') + + installCommands "${commandPackage[@]}" +} + +function installPIPPackage() +{ + local -r package="${1}" + + if [[ "$(isPIPPackageInstall "${package}")" = 'true' ]] + then + debug "PIP Package '${package}' found" + else + echo -e "\033[1;35m\nInstalling PIP package '${package}'\033[0m" + pip install "${package}" + fi +} + +function installUnzipCommand() +{ + local -r commandPackage=('unzip' 'unzip') + + installCommands "${commandPackage[@]}" +} + +function isAptGetPackageInstall() +{ + local -r package="$(escapeGrepSearchPattern "${1}")" + + local -r found="$(dpkg --get-selections | grep -E -o "^${package}(:amd64)*\s+install$")" + + if [[ "$(isEmptyString "${found}")" = 'true' ]] + then + echo 'false' && return 1 + fi + + echo 'true' && return 0 +} + +function isPIPPackageInstall() +{ + local -r package="$(escapeGrepSearchPattern "${1}")" + + # Install PIP + + installPIPCommand > '/dev/null' + + # Check Command + + if [[ "$(existCommand 'pip')" = 'false' ]] + then + fatal 'FATAL : command python-pip not found' + fi + + local -r found="$(pip list | grep -E -o "^${package}\s+\(.*\)$")" + + if [[ "$(isEmptyString "${found}")" = 'true' ]] + then + echo 'false' && return 1 + fi + + echo 'true' && return 0 +} + +function runAptGetUpdate() +{ + local updateInterval="${1}" + + if [[ "$(isUbuntuDistributor)" = 'true' ]] + then + local -r lastAptGetUpdate="$(getLastAptGetUpdate)" + + if [[ "$(isEmptyString "${updateInterval}")" = 'true' ]] + then + # Default To 24 hours + updateInterval="$((24 * 60 * 60))" + fi + + if [[ "${lastAptGetUpdate}" -gt "${updateInterval}" ]] + then + info 'apt-get update' + apt-get update -m + else + local -r lastUpdate="$(date -u -d @"${lastAptGetUpdate}" +'%-Hh %-Mm %-Ss')" + + info "\nSkip apt-get update because its last run was '${lastUpdate}' ago" + fi + fi +} + +function runUpgrade() +{ + header 'UPGRADING SYSTEM' + + if [[ "$(isUbuntuDistributor)" = 'true' ]] + then + runAptGetUpdate '' + + info '\napt-get upgrade' + DEBIAN_FRONTEND='noninteractive' apt-get --fix-missing -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' upgrade + + info '\napt-get dist-upgrade' + DEBIAN_FRONTEND='noninteractive' apt-get --fix-missing -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' dist-upgrade + + info '\napt-get autoremove' + DEBIAN_FRONTEND='noninteractive' apt-get --fix-missing -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' autoremove + + info '\napt-get clean' + DEBIAN_FRONTEND='noninteractive' apt-get --fix-missing -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' clean + + info '\napt-get autoclean' + DEBIAN_FRONTEND='noninteractive' apt-get --fix-missing -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' autoclean + elif [[ "$(isAmazonLinuxDistributor)" = 'true' || "$(isCentOSDistributor)" = 'true' || "$(isRedHatDistributor)" = 'true' ]] + then + yum -y --security update + yum -y update --nogpgcheck --skip-broken + fi +} + +function upgradePIPPackage() +{ + local -r package="${1}" + + if [[ "$(isPIPPackageInstall "${package}")" = 'true' ]] + then + echo -e "\033[1;35mUpgrading PIP package '${package}'\033[0m" + pip install --upgrade "${package}" + else + debug "PIP Package '${package}' not found" + fi +} + +##################### +# SERVICE UTILITIES # +##################### + +function disableService() +{ + local -r serviceName="${1}" + + checkNonEmptyString "${serviceName}" 'undefined service name' + + if [[ "$(existCommand 'systemctl')" = 'true' ]] + then + header "DISABLE SYSTEMD ${serviceName}" + + systemctl daemon-reload + systemctl disable "${serviceName}" + systemctl stop "${serviceName}" || true + else + header "DISABLE SERVICE ${serviceName}" + + chkconfig "${serviceName}" off + service "${serviceName}" stop || true + fi + + statusService "${serviceName}" +} + +function enableService() +{ + local -r serviceName="${1}" + + checkNonEmptyString "${serviceName}" 'undefined service name' + + if [[ "$(existCommand 'systemctl')" = 'true' ]] + then + header "ENABLE SYSTEMD ${serviceName}" + + systemctl daemon-reload + systemctl enable "${serviceName}" || true + else + header "ENABLE SERVICE ${serviceName}" + + chkconfig "${serviceName}" on + fi + + statusService "${serviceName}" +} + +function restartService() +{ + local -r serviceName="${1}" + + checkNonEmptyString "${serviceName}" 'undefined service name' + + stopService "${serviceName}" + startService "${serviceName}" +} + +function startService() +{ + local -r serviceName="${1}" + + checkNonEmptyString "${serviceName}" 'undefined service name' + + if [[ "$(existCommand 'systemctl')" = 'true' ]] + then + header "STARTING SYSTEMD ${serviceName}" + + systemctl daemon-reload + systemctl enable "${serviceName}" || true + systemctl start "${serviceName}" + else + header "STARTING SERVICE ${serviceName}" + + chkconfig "${serviceName}" on + service "${serviceName}" start + fi + + statusService "${serviceName}" +} + +function statusService() +{ + local -r serviceName="${1}" + + checkNonEmptyString "${serviceName}" 'undefined service name' + + if [[ "$(existCommand 'systemctl')" = 'true' ]] + then + header "STATUS SYSTEMD ${serviceName}" + + systemctl status "${serviceName}" --full --no-pager || true + else + header "STATUS SERVICE ${serviceName}" + + service "${serviceName}" status || true + fi +} + +function stopService() +{ + local -r serviceName="${1}" + + checkNonEmptyString "${serviceName}" 'undefined service name' + + if [[ "$(existCommand 'systemctl')" = 'true' ]] + then + header "STOPPING SYSTEMD ${serviceName}" + + systemctl daemon-reload + systemctl stop "${serviceName}" || true + else + header "STOPPING SERVICE ${serviceName}" + + service "${serviceName}" stop || true + fi + + statusService "${serviceName}" +} + +#################### +# STRING UTILITIES # +#################### + +function checkNonEmptyString() +{ + local -r string="${1}" + local -r errorMessage="${2}" + + if [[ "$(isEmptyString "${string}")" = 'true' ]] + then + if [[ "$(isEmptyString "${errorMessage}")" = 'true' ]] + then + fatal '\nFATAL : empty value detected' + fi + + fatal "\nFATAL : ${errorMessage}" + fi +} + +function checkTrueFalseString() +{ + local -r string="${1}" + local -r errorMessage="${2}" + + if [[ "${string}" != 'true' && "${string}" != 'false' ]] + then + if [[ "$(isEmptyString "${errorMessage}")" = 'true' ]] + then + fatal "\nFATAL : '${string}' is not 'true' or 'false'" + fi + + fatal "\nFATAL : ${errorMessage}" + fi +} + +function debug() +{ + local -r message="${1}" + + if [[ "$(isEmptyString "${message}")" = 'false' ]] + then + echo -e "\033[1;34m${message}\033[0m" 2>&1 + fi +} + +function deleteSpaces() +{ + local -r content="${1}" + + replaceString "${content}" ' ' '' +} + +function displayVersion() +{ + local -r message="${1}" + local -r applicationName="${2}" + + if [[ "$(isEmptyString "${applicationName}")" = 'true' ]] + then + header 'DISPLAYING VERSION' + else + header "DISPLAYING ${applicationName} VERSION" + fi + + info "${message}" +} + +function encodeURL() +{ + local -r url="${1}" + + local i=0 + + for ((i = 0; i < ${#url}; i++)) + do + local walker='' + walker="${url:i:1}" + + case "${walker}" in + [a-zA-Z0-9.~_-]) + printf '%s' "${walker}" + ;; + ' ') + printf + + ;; + *) + printf '%%%X' "'${walker}" + ;; + esac + done +} + +function error() +{ + local -r message="${1}" + + if [[ "$(isEmptyString "${message}")" = 'false' ]] + then + echo -e "\033[1;31m${message}\033[0m" 1>&2 + fi +} + +function escapeGrepSearchPattern() +{ + local -r searchPattern="${1}" + + sed 's/[]\.|$(){}?+*^]/\\&/g' <<< "${searchPattern}" +} + +function escapeSearchPattern() +{ + local -r searchPattern="${1}" + + sed -e "s@\@@\\\\\\@@g" -e "s@\[@\\\\[@g" -e "s@\*@\\\\*@g" -e "s@\%@\\\\%@g" <<< "${searchPattern}" +} + +function fatal() +{ + local -r message="${1}" + + error "${message}" + exit 1 +} + +function formatPath() +{ + local path="${1}" + + while [[ "$(grep -F '//' <<< "${path}")" != '' ]] + do + path="$(sed -e 's/\/\/*/\//g' <<< "${path}")" + done + + sed -e 's/\/$//g' <<< "${path}" +} + +function header() +{ + local -r title="${1}" + + if [[ "$(isEmptyString "${title}")" = 'false' ]] + then + echo -e "\n\033[1;33m>>>>>>>>>> \033[1;4;35m${title}\033[0m \033[1;33m<<<<<<<<<<\033[0m\n" + fi +} + +function indentString() +{ + local -r indentString="$(escapeSearchPattern "${1}")" + local -r string="$(escapeSearchPattern "${2}")" + + sed "s@^@${indentString}@g" <<< "${string}" +} + +function info() +{ + local -r message="${1}" + + if [[ "$(isEmptyString "${message}")" = 'false' ]] + then + echo -e "\033[1;36m${message}\033[0m" 2>&1 + fi +} + +function invertTrueFalseString() +{ + local -r string="${1}" + + checkTrueFalseString "${string}" + + if [[ "${string}" = 'true' ]] + then + echo 'false' && return 1 + fi + + echo 'true' && return 0 +} + +function isEmptyString() +{ + local -r string="${1}" + + if [[ "$(trimString "${string}")" = '' ]] + then + echo 'true' && return 0 + fi + + echo 'false' && return 1 +} + +function postUpMessage() +{ + echo -e "\n\033[1;32m¯\_(ツ)_/¯\033[0m" +} + +function printTable() +{ + local -r delimiter="${1}" + local -r tableData="$(removeEmptyLines "${2}")" + local -r colorHeader="${3}" + local -r displayTotalCount="${4}" + + if [[ "${delimiter}" != '' && "$(isEmptyString "${tableData}")" = 'false' ]] + then + local -r numberOfLines="$(trimString "$(wc -l <<< "${tableData}")")" + + if [[ "${numberOfLines}" -gt '0' ]] + then + local table='' + local i=1 + + for ((i = 1; i <= "${numberOfLines}"; i = i + 1)) + do + local line='' + line="$(sed "${i}q;d" <<< "${tableData}")" + + local numberOfColumns=0 + numberOfColumns="$(awk -F "${delimiter}" '{print NF}' <<< "${line}")" + + # Add Line Delimiter + + if [[ "${i}" -eq '1' ]] + then + table="${table}$(printf '%s#+' "$(repeatString '#+' "${numberOfColumns}")")" + fi + + # Add Header Or Body + + table="${table}\n" + + local j=1 + + for ((j = 1; j <= "${numberOfColumns}"; j = j + 1)) + do + table="${table}$(printf '#| %s' "$(cut -d "${delimiter}" -f "${j}" <<< "${line}")")" + done + + table="${table}#|\n" + + # Add Line Delimiter + + if [[ "${i}" -eq '1' ]] || [[ "${numberOfLines}" -gt '1' && "${i}" -eq "${numberOfLines}" ]] + then + table="${table}$(printf '%s#+' "$(repeatString '#+' "${numberOfColumns}")")" + fi + done + + if [[ "$(isEmptyString "${table}")" = 'false' ]] + then + local output='' + output="$(echo -e "${table}" | column -s '#' -t | awk '/^\+/{gsub(" ", "-", $0)}1')" + + if [[ "${colorHeader}" = 'true' ]] + then + echo -e "\033[1;32m$(head -n 3 <<< "${output}")\033[0m" + tail -n +4 <<< "${output}" + else + echo "${output}" + fi + fi + fi + + if [[ "${displayTotalCount}" = 'true' && "${numberOfLines}" -ge '0' ]] + then + echo -e "\n\033[1;36mTOTAL ROWS : $((numberOfLines - 1))\033[0m" + fi + fi +} + +function removeEmptyLines() +{ + local -r content="${1}" + + echo -e "${content}" | sed '/^\s*$/d' +} + +function repeatString() +{ + local -r string="${1}" + local -r numberToRepeat="${2}" + + if [[ "${string}" != '' && "$(isPositiveInteger "${numberToRepeat}")" = 'true' ]] + then + local -r result="$(printf "%${numberToRepeat}s")" + echo -e "${result// /${string}}" + fi +} + +function replaceString() +{ + local -r content="${1}" + local -r oldValue="$(escapeSearchPattern "${2}")" + local -r newValue="$(escapeSearchPattern "${3}")" + + sed "s@${oldValue}@${newValue}@g" <<< "${content}" +} + +function stringToNumber() +{ + local -r string="${1}" + + checkNonEmptyString "${string}" 'undefined string' + + if [[ "$(existCommand 'md5')" = 'true' ]] + then + md5 <<< "${string}" | tr -cd '0-9' + elif [[ "$(existCommand 'md5sum')" = 'true' ]] + then + md5sum <<< "${string}" | tr -cd '0-9' + else + fatal '\nFATAL : md5 or md5sum command not found' + fi +} + +function stringToSearchPattern() +{ + local -r string="$(trimString "${1}")" + + if [[ "$(isEmptyString "${string}")" = 'true' ]] + then + echo "${string}" + else + echo "^\s*$(sed -e 's/\s\+/\\s+/g' <<< "$(escapeSearchPattern "${string}")")\s*$" + fi +} + +function trimString() +{ + local -r string="${1}" + + sed 's,^[[:blank:]]*,,' <<< "${string}" | sed 's,[[:blank:]]*$,,' +} + +function warn() +{ + local -r message="${1}" + + if [[ "$(isEmptyString "${message}")" = 'false' ]] + then + echo -e "\033[1;33m${message}\033[0m" 1>&2 + fi +} + +#################### +# SYSTEM UTILITIES # +#################### + +function addSwapSpace() +{ + local swapSize="${1}" + local swapFile="${2}" + + header 'ADDING SWAP SPACE' + + # Set Default Values + + if [[ "$(isEmptyString "${swapSize}")" = 'true' ]] + then + swapSize='1024000' + fi + + if [[ "$(isEmptyString "${swapFile}")" = 'true' ]] + then + swapFile='/mnt/swapfile' + fi + + if [[ -f "${swapFile}" ]] + then + swapoff "${swapFile}" + fi + + rm -f "${swapFile}" + touch "${swapFile}" + + # Create Swap File + + dd if=/dev/zero of="${swapFile}" bs=1024 count="${swapSize}" + mkswap "${swapFile}" + chmod 600 "${swapFile}" + swapon "${swapFile}" + + # Config Swap File System + + local -r fstabConfig="${swapFile} swap swap defaults 0 0" + + appendToFileIfNotFound '/etc/fstab' "$(stringToSearchPattern "${fstabConfig}")" "${fstabConfig}" 'true' 'false' 'true' + + # Display Swap Status + + free -m +} + +function checkExistCommand() +{ + local -r command="${1}" + local -r errorMessage="${2}" + + if [[ "$(existCommand "${command}")" = 'false' ]] + then + if [[ "$(isEmptyString "${errorMessage}")" = 'true' ]] + then + fatal "\nFATAL : command '${command}' not found" + fi + + fatal "\nFATAL : ${errorMessage}" + fi +} + +function checkRequirePorts() +{ + local -r ports=("${@}") + + installPackages 'lsof' + + local -r headerRegex='^COMMAND\s\+PID\s\+USER\s\+FD\s\+TYPE\s\+DEVICE\s\+SIZE\/OFF\s\+NODE\s\+NAME$' + local -r status="$(lsof -i -n -P | grep "\( (LISTEN)$\)\|\(${headerRegex}\)")" + + local open='' + local port='' + + for port in "${ports[@]}" + do + local found='' + found="$(grep -i ":${port} (LISTEN)$" <<< "${status}" || echo)" + + if [[ "$(isEmptyString "${found}")" = 'false' ]] + then + open="${open}\n${found}" + fi + done + + if [[ "$(isEmptyString "${open}")" = 'false' ]] + then + echo -e "\033[1;31mFollowing ports are still opened. Make sure you uninstall or stop them before a new installation!\033[0m" + echo -e -n "\033[1;34m\n$(grep "${headerRegex}" <<< "${status}")\033[0m" + echo -e "\033[1;36m${open}\033[0m\n" + + exit 1 + fi +} + +function displayOpenPorts() +{ + local -r sleepTimeInSecond="${1}" + + installPackages 'lsof' + + header 'DISPLAYING OPEN PORTS' + + if [[ "$(isEmptyString "${sleepTimeInSecond}")" = 'false' ]] + then + sleep "${sleepTimeInSecond}" + fi + + lsof -i -n -P | grep -i ' (LISTEN)$' | sort -f +} + +function existCommand() +{ + local -r command="${1}" + + if [[ "$(which "${command}" 2> '/dev/null')" = '' ]] + then + echo 'false' && return 1 + fi + + echo 'true' && return 0 +} + +function existDisk() +{ + local -r disk="${1}" + + local -r foundDisk="$(fdisk -l "${disk}" 2> '/dev/null' | grep -E -i -o "^Disk\s+$(escapeGrepSearchPattern "${disk}"): ")" + + if [[ "$(isEmptyString "${disk}")" = 'false' && "$(isEmptyString "${foundDisk}")" = 'false' ]] + then + echo 'true' && return 0 + fi + + echo 'false' && return 1 +} + +function existDiskMount() +{ + local -r disk="$(escapeGrepSearchPattern "${1}")" + local -r mountOn="$(escapeGrepSearchPattern "${2}")" + + local -r foundMount="$(df | grep -E "^${disk}\s+.*\s+${mountOn}$")" + + if [[ "$(isEmptyString "${foundMount}")" = 'true' ]] + then + echo 'false' && return 1 + fi + + echo 'true' && return 0 +} + +function existModule() +{ + local -r module="${1}" + + checkNonEmptyString "${module}" 'undefined module' + + if [[ "$(lsmod | awk '{ print $1 }' | grep -F -o "${module}")" = '' ]] + then + echo 'false' && return 1 + fi + + echo 'true' && return 0 +} + +function existMount() +{ + local -r mountOn="$(escapeGrepSearchPattern "${1}")" + + local -r foundMount="$(df | grep -E ".*\s+${mountOn}$")" + + if [[ "$(isEmptyString "${foundMount}")" = 'true' ]] + then + echo 'false' && return 1 + fi + + echo 'true' && return 0 +} + +function flushFirewall() +{ + header 'FLUSHING FIREWALL' + + iptables -P INPUT ACCEPT + iptables -P FORWARD ACCEPT + iptables -P OUTPUT ACCEPT + + iptables -t nat -F + iptables -t mangle -F + iptables -F + iptables -X + + iptables --list + + saveFirewall +} + +function isPortOpen() +{ + local -r port="$(escapeGrepSearchPattern "${1}")" + + checkNonEmptyString "${port}" 'undefined port' + + if [[ "$(isAmazonLinuxDistributor)" = 'true' || "$(isRedHatDistributor)" = 'true' || "$(isUbuntuDistributor)" = 'true' ]] + then + local -r process="$(netstat -l -n -t -u | grep -E ":${port}\s+" | head -1)" + elif [[ "$(isCentOSDistributor)" = 'true' || "$(isMacOperatingSystem)" = 'true' ]] + then + if [[ "$(isCentOSDistributor)" = 'true' ]] + then + installPackages 'lsof' + fi + + local -r process="$(lsof -i -n -P | grep -E -i ":${port}\s+\(LISTEN\)$" | head -1)" + else + fatal '\nFATAL : only support Amazon-Linux, CentOS, Mac, RedHat, or Ubuntu OS' + fi + + if [[ "$(isEmptyString "${process}")" = 'true' ]] + then + echo 'false' && return 1 + fi + + echo 'true' && return 0 +} + +function redirectJDKTMPDir() +{ + local -r option="_JAVA_OPTIONS='-Djava.io.tmpdir=/var/tmp'" + + appendToFileIfNotFound '/etc/environment' "${option}" "${option}" 'false' 'false' 'true' + appendToFileIfNotFound '/etc/profile' "${option}" "${option}" 'false' 'false' 'true' +} + +function remountTMP() +{ + header 'RE-MOUNTING TMP' + + if [[ "$(existMount '/tmp')" = 'true' ]] + then + mount -o 'remount,rw,exec,nosuid' -v '/tmp' + else + warn 'WARN : mount /tmp not found' + fi +} + +function saveFirewall() +{ + header 'SAVING FIREWALL' + + local ruleFile='' + + for ruleFile in '/etc/iptables/rules.v4' '/etc/iptables/rules.v6' '/etc/sysconfig/iptables' '/etc/sysconfig/ip6tables' + do + if [[ -f "${ruleFile}" ]] + then + if [[ "$(grep -F '6' <<< "${ruleFile}")" = '' ]] + then + iptables-save > "${ruleFile}" + else + ip6tables-save > "${ruleFile}" + fi + + info "${ruleFile}" + cat "${ruleFile}" + echo + fi + done +} + +############################ +# USER AND GROUP UTILITIES # +############################ + +function addUser() +{ + local -r userLogin="${1}" + local -r groupName="${2}" + local -r createHome="${3}" + local -r systemAccount="${4}" + local -r allowLogin="${5}" + + checkNonEmptyString "${userLogin}" 'undefined user login' + checkNonEmptyString "${groupName}" 'undefined group name' + + # Options + + if [[ "${createHome}" = 'true' ]] + then + local -r createHomeOption=('-m') + else + local -r createHomeOption=('-M') + fi + + if [[ "${allowLogin}" = 'true' ]] + then + local -r allowLoginOption=('-s' '/bin/bash') + else + local -r allowLoginOption=('-s' '/bin/false') + fi + + # Add Group + + groupadd -f -r "${groupName}" + + # Add User + + if [[ "$(existUserLogin "${userLogin}")" = 'true' ]] + then + if [[ "$(isUserLoginInGroupName "${userLogin}" "${groupName}")" = 'false' ]] + then + usermod -a -G "${groupName}" "${userLogin}" + fi + + # Not Exist Home + + if [[ "${createHome}" = 'true' ]] + then + local -r userHome="$(getUserHomeFolder "${userLogin}")" + + if [[ "$(isEmptyString "${userHome}")" = 'true' || ! -d "${userHome}" ]] + then + mkdir -m 700 -p "/home/${userLogin}" + chown -R "${userLogin}:${groupName}" "/home/${userLogin}" + fi + fi + else + if [[ "${systemAccount}" = 'true' ]] + then + useradd "${createHomeOption[@]}" -r "${allowLoginOption[@]}" -g "${groupName}" "${userLogin}" + else + useradd "${createHomeOption[@]}" "${allowLoginOption[@]}" -g "${groupName}" "${userLogin}" + fi + fi +} + +function addUserAuthorizedKey() +{ + local -r userLogin="${1}" + local -r groupName="${2}" + local -r sshRSA="${3}" + + configUserSSH "${userLogin}" "${groupName}" "${sshRSA}" 'authorized_keys' +} + +function addUserSSHKnownHost() +{ + local -r userLogin="${1}" + local -r groupName="${2}" + local -r sshRSA="${3}" + + configUserSSH "${userLogin}" "${groupName}" "${sshRSA}" 'known_hosts' +} + +function addUserToSudoWithoutPassword() +{ + local -r userLogin="${1}" + + echo "${userLogin} ALL=(ALL) NOPASSWD:ALL" > "/etc/sudoers.d/${userLogin}" + chmod 440 "/etc/sudoers.d/${userLogin}" +} + +function checkExistGroupName() +{ + local -r groupName="${1}" + + if [[ "$(existGroupName "${groupName}")" = 'false' ]] + then + fatal "\nFATAL : group name '${groupName}' not found" + fi +} + +function checkExistUserLogin() +{ + local -r userLogin="${1}" + + if [[ "$(existUserLogin "${userLogin}")" = 'false' ]] + then + fatal "\nFATAL : user login '${userLogin}' not found" + fi +} + +function checkRequireNonRootUser() +{ + if [[ "$(whoami)" = 'root' ]] + then + fatal '\nFATAL : non root login required' + fi +} + +function checkRequireRootUser() +{ + checkRequireUserLogin 'root' +} + +function checkRequireUserLogin() +{ + local -r userLogin="${1}" + + if [[ "$(whoami)" != "${userLogin}" ]] + then + fatal "\nFATAL : user login '${userLogin}' required" + fi +} + +function configUserGIT() +{ + local -r userLogin="${1}" + local -r gitUserName="${2}" + local -r gitUserEmail="${3}" + + header "CONFIGURING GIT FOR USER ${userLogin}" + + checkExistUserLogin "${userLogin}" + checkNonEmptyString "${gitUserName}" 'undefined git user name' + checkNonEmptyString "${gitUserEmail}" 'undefined git user email' + + su -l "${userLogin}" -c "git config --global user.name '${gitUserName}'" + su -l "${userLogin}" -c "git config --global user.email '${gitUserEmail}'" + su -l "${userLogin}" -c 'git config --global push.default simple' + + info "$(su -l "${userLogin}" -c 'git config --list')" +} + +function configUserSSH() +{ + local -r userLogin="${1}" + local -r groupName="${2}" + local -r sshRSA="${3}" + local -r configFileName="${4}" + + header "CONFIGURING ${configFileName} FOR USER ${userLogin}" + + checkExistUserLogin "${userLogin}" + checkExistGroupName "${groupName}" + checkNonEmptyString "${sshRSA}" 'undefined SSH-RSA' + checkNonEmptyString "${configFileName}" 'undefined config file' + + local -r userHome="$(getUserHomeFolder "${userLogin}")" + + checkExistFolder "${userHome}" + + mkdir -m 700 -p "${userHome}/.ssh" + touch "${userHome}/.ssh/${configFileName}" + appendToFileIfNotFound "${userHome}/.ssh/${configFileName}" "${sshRSA}" "${sshRSA}" 'false' 'false' 'false' + chmod 600 "${userHome}/.ssh/${configFileName}" + + chown -R "${userLogin}:${groupName}" "${userHome}/.ssh" + + cat "${userHome}/.ssh/${configFileName}" +} + +function deleteUser() +{ + local -r userLogin="${1}" + + if [[ "$(existUserLogin "${userLogin}")" = 'true' ]] + then + userdel -f -r "${userLogin}" 2> '/dev/null' || true + fi +} + +function existGroupName() +{ + local -r group="${1}" + + if [[ "$(grep -E -o "^${group}:" '/etc/group')" = '' ]] + then + echo 'false' && return 1 + fi + + echo 'true' && return 0 +} + +function existUserLogin() +{ + local -r user="${1}" + + if ( id -u "${user}" > '/dev/null' 2>&1 ) + then + echo 'true' && return 0 + fi + + echo 'false' && return 1 +} + +function generateSSHPublicKeyFromPrivateKey() +{ + local -r userLogin="${1}" + local groupName="${2}" + + # Set Default + + if [[ "$(isEmptyString "${groupName}")" = 'true' ]] + then + groupName="${userLogin}" + fi + + # Validate Input + + checkExistUserLogin "${userLogin}" + checkExistGroupName "${groupName}" + + local -r userHome="$(getUserHomeFolder "${userLogin}")" + + checkExistFile "${userHome}/.ssh/id_rsa" + + # Generate SSH Public Key + + header "GENERATING SSH PUBLIC KEY FOR USER '${userLogin}' FROM PRIVATE KEY" + + rm -f "${userHome}/.ssh/id_rsa.pub" + su -l "${userLogin}" -c "ssh-keygen -f '${userHome}/.ssh/id_rsa' -y > '${userHome}/.ssh/id_rsa.pub'" + chmod 600 "${userHome}/.ssh/id_rsa.pub" + chown "${userLogin}:${groupName}" "${userHome}/.ssh/id_rsa.pub" + + cat "${userHome}/.ssh/id_rsa.pub" +} + +function generateUserSSHKey() +{ + local -r userLogin="${1}" + local groupName="${2}" + + # Set Default + + if [[ "$(isEmptyString "${groupName}")" = 'true' ]] + then + groupName="${userLogin}" + fi + + # Validate Input + + checkExistUserLogin "${userLogin}" + checkExistGroupName "${groupName}" + + local -r userHome="$(getUserHomeFolder "${userLogin}")" + + checkExistFolder "${userHome}" + + # Generate SSH Key + + header "GENERATING SSH KEY FOR USER '${userLogin}'" + + rm -f "${userHome}/.ssh/id_rsa" "${userHome}/.ssh/id_rsa.pub" + mkdir -m 700 -p "${userHome}/.ssh" + chown "${userLogin}:${groupName}" "${userHome}/.ssh" + + su -l "${userLogin}" -c "ssh-keygen -q -t rsa -N '' -f '${userHome}/.ssh/id_rsa'" + chmod 600 "${userHome}/.ssh/id_rsa" "${userHome}/.ssh/id_rsa.pub" + chown "${userLogin}:${groupName}" "${userHome}/.ssh/id_rsa" "${userHome}/.ssh/id_rsa.pub" + + cat "${userHome}/.ssh/id_rsa.pub" +} + +function getCurrentUserHomeFolder() +{ + getUserHomeFolder "$(whoami)" +} + +function getProfileFilePath() +{ + local -r user="${1}" + + local -r userHome="$(getUserHomeFolder "${user}")" + + if [[ "$(isEmptyString "${userHome}")" = 'false' && -d "${userHome}" ]] + then + local -r bashProfileFilePath="${userHome}/.bash_profile" + local -r profileFilePath="${userHome}/.profile" + + if [[ ! -f "${bashProfileFilePath}" && -f "${profileFilePath}" ]] + then + echo "${profileFilePath}" + else + echo "${bashProfileFilePath}" + fi + fi +} + +function getUserGroupName() +{ + local -r userLogin="${1}" + + checkExistUserLogin "${userLogin}" + + id -g -n "${userLogin}" +} + +function getUserHomeFolder() +{ + local -r user="${1}" + + if [[ "$(isEmptyString "${user}")" = 'false' ]] + then + local -r homeFolder="$(eval "echo ~${user}")" + + if [[ "${homeFolder}" = "\~${user}" ]] + then + echo + else + echo "${homeFolder}" + fi + else + echo + fi +} + +function isUserLoginInGroupName() +{ + local -r userLogin="${1}" + local -r groupName="${2}" + + checkNonEmptyString "${userLogin}" 'undefined user login' + checkNonEmptyString "${groupName}" 'undefined group name' + + if [[ "$(existUserLogin "${userLogin}")" = 'true' ]] && [[ "$(groups "${userLogin}" | grep "\b${groupName}\b")" != '' ]] + then + echo 'true' && return 0 + fi + + echo 'false' && return 1 +} \ No newline at end of file diff --git a/shell/yaml.sh b/shell/yaml.sh new file mode 100644 index 0000000..9e68b10 --- /dev/null +++ b/shell/yaml.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +parse_yaml() { + local prefix=$2 + local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034') + sed -ne "s|^\($s\)\($w\)$s:$s\"\(.*\)\"$s\$|\1$fs\2$fs\3|p" \ + -e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" $1 | + awk -F$fs '{ + indent = length($1)/2; + vname[indent] = $2; + for (i in vname) {if (i > indent) {delete vname[i]}} + if (length($3) > 0) { + vn=""; for (i=0; i