diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a34ee8e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +centos_django.step \ No newline at end of file diff --git a/centos.conf b/centos.conf new file mode 100755 index 0000000..ba1df70 --- /dev/null +++ b/centos.conf @@ -0,0 +1,54 @@ +# ATENÇÃO, NÃO DEVE HAVER SEPARAÇÃO ENTRE O NOME DA VARIÁVEL E O VALOR DA VARIÁVEL APÓS O SINAL DE IGUALDADE +# ATTENTION, NO SPACES BETWEEN VAR NAME AND VAR CONTENT + +# Nome do usuário no sistema +# Sistem's username +USERNAME="lucas" + +# Nome do App no repositório +# App name on repo +APP="3m" + +# Nome do ambiente virtual python +# Virtual environment name +VENV=".venv" + +# Nome de usuário do repositório +# Git username +GIT_USER="lucas" + +# Endereço do projeto no repositório +# Git project url +GITURL="https://git.lucasf.dev/$GIT_USER/$APP.git" + +# IP público ou domínio +# Public IP address or domain +PUB_IP="" + +# Nome da pasta que está no mesmo nível do arquivo wsgi.py +# Folder name that is at the same level as wsgi.py file +WSGI_FOLDER_NAME="3m" + +# Nome desejado para o serviço no Systemctl +# Systemctl desirable name +SERVICE="app.service" + +# Descrição do serviço no Systemctl +# Systemctl service description +DESCRIPTION="Django VPS uWSGI Emperor" + +# Adicionar o certificado digital na aplicação com Certbot. Escreva sim, se já possuir um domínio na internet +# Apply certbot ssl certificate, hit yes if you already have a domain +CERTBOT="no" + +# Adicionar renovação do certbot no crontab. Escreva sim, apenas se já possuir um domínio na internet +# Create a renew certbot cron job, hit yes if you already have a domain +CRON_CERTBOT="no" + +# Incrementar IP em ALLOWED_HOSTS no arquivo .env caso não tenha incrementado +# Increase ALLOWED_HOSTS IP on .env file +IP_INCRE="yes" + +# Mensagens em Português Brasil +# PT_BR messages +PTBR="no" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..7345537 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,14 @@ +[tool.poetry] +name = "django_centos_deploy" +version = "0.1.0" +description = "" +authors = ["Lucas F. "] + +[tool.poetry.dependencies] +python = "^3.9" + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..472f671 --- /dev/null +++ b/run.sh @@ -0,0 +1,495 @@ +#!/bin/bash + +# =============================================== +# Esse script foi desenvolvido por Lucas F. +# This script was developed by Lucas F. +# +# Novembro de 2019 +# 2019, November +# +# =============================================== + +# Arquivo de configuração +# Config filename +ARQ_CONF="centos.conf" + +# Nome do usuário no sistema +# Sistem's username +USERNAME="centos" + +# Nome do App no repositório +# App name on repo +APP="appname" + +# Nome do ambiente virtual python +# Virtual environment name +VENV=".venv" + +# Nome de usuário do repositório +# Git username +GIT_USER="username" + +# Endereço do projeto no repositório +# Git project url +GITURL="https://repositório.com/$GIT_USER/$APP.git" + +# IP público ou domínio +# Public IP address or domain +PUB_IP="" + +# Nome da pasta que está no mesmo nível do arquivo wsgi.py +# Folder name that is at the same level as wsgi.py file +WSGI_FOLDER_NAME="appdirname" + +# Nome desejado para o serviço no Systemctl +# Systemctl desirable name +SERVICE="app.service" + +# Descrição do serviço no Systemctl +# Systemctl service description +DESCRIPTION="Django VPS uWSGI Emperor" + +# Adicionar o certificado digital na aplicação com Certbot. Escreva sim, se já possuir um domínio na internet +# Apply certbot ssl certificate, hit yes if you already have a domain +CERTBOT="no" + +# Adicionar renovação do certbot no crontab. Escreva sim, apenas se já possuir um domínio na internet +# Create a renew certbot cron job, hit yes if you already have a domain +CRON_CERTBOT="no" + +# Incrementar IP em ALLOWED_HOSTS caso não tenha incrementado +# Increase ALLOWED_HOSTS IP on .env file +IP_INCRE="no" + +# Mensagens em Português Brasil +# PT_BR messages +PTBR="yes" + +HAS_ERROS="no" +ERRORS=() + +declare -A MSGS +declare -A MSG_PT +MSG_PT[errors]="Por favor verifique os seguintes erros:" +MSG_PT[before]="Antes de começarmos verifique se as variáveis estão corretas:" +MSG_PT[entry]="Digite sim ou s para continuar (sim/não): " +MSG_PT[error_root]="Você precisa executar o script como root" +MSG_PT[error_requirements]="Não encontrei o arquivo requirements.txt" +MSG_PT[error_env]="Não encontrei o arquivo .env" +MSG_PT[error_allwhost]="Não encontrei a variável ALLOWED_HOSTS, você lembrou de extrair do settings.py?" +MSG_PT[error_debug]="Não encontrei a variável DEBUG, você lembrou de extrair do settings.py?" +MSG_PT[repo]="Por favor verificar o repositório ou suas credenciais" +MSG_PT[done]="FEITO" +MSG_PT[fail]="FALHOU" + +declare -A MSG_EN +MSG_EN[errors]="Please check this errors:" +MSG_EN[before]="Please check the variables before starting:" +MSG_EN[entry]="Shall we proceed? (yes/no): " +MSG_EN[error_root]="You must be root" +MSG_EN[error_req]="Can't find requirements.txt file" +MSG_EN[error_env]="Can't find .env file" +MSG_EN[error_allwhost]="Can't find ALLOWED_HOSTS variable did you extract it from settings.py?" +MSG_EN[error_debug]="Can't find DEBUG variable did you extract it from settings.py?" +MSG_EN[repo]="Please check your repo or credentials" +MSG_EN[done]="DONE" +MSG_EN[fail]="FAIL" + +STEP=0 +SCRIPT_PATH=$(dirname $0) +SCRIPT_STEP_FILE="$SCRIPT_PATH/centos_django.step" + +# Lê o arquivo de configuração e atualiza os valores das variáveis +# Read config file and set all variables +if [[ -f "$SCRIPT_PATH/$ARQ_CONF" ]]; then + . "$SCRIPT_PATH/$ARQ_CONF" +fi + +# Lê o arquivo de passos e atualiza o valor da variável +# Read step file and update the value +if [[ -f "$SCRIPT_STEP_FILE" ]]; then + . "$SCRIPT_STEP_FILE" +fi +echo "step>$STEP" +# Verifica se deve exibir as mensagens em português do Brasil +# Check if the language pt_br has been chosen +if [[ ${PTBR,,} == "yes" ]] || [[ ${PTBR,,} == "sim" ]]; then + for i in ${!MSG_PT[@]}; do + MSGS[$i]=${MSG_PT[$i]} + done +else + for i in ${!MSG_EN[@]}; do + MSGS[$i]=${MSG_EN[$i]} + done +fi + +# Verifica se o usuário ativo é o root +# Verify if is a root user +if [[ "$EUID" -ne 0 ]];then + HAS_ERROS="yes" + ERRORS+=("${MSGS[error_root]}") +fi + +# Verifica a existência do usuário no sistema +# Check if user exists +CNU=$(grep -c $USERNAME /etc/passwd) +if [[ $CNU -lt 1 ]]; then + echo "precisa criar o usuário" + #useradd --create-home $USERNAME +fi + +# Obtém o IP +# Get IP address +if [[ -n $(man hostname | awk '{RS="";FS="\n"} /-i,/ {print}') ]];then + PUB_IP=$(hostname -i | cut -d" " -f1) +else + PUB_IP=$(hostname -I | cut -d" " -f1) +fi + +update_step_error() { + STEP="$x" + echo "STEP=$STEP" > "$SCRIPT_STEP_FILE" + exit 1 +} + +# Exibe todas as variáveis +# Display all variables +DISPLAY=" +\033[33mUSERNAME \033[0m= \033[37m$USERNAME +\033[33mAPP \033[0m= \033[37m$APP +\033[33mVENV \033[0m= \033[37m$VENV +\033[33mGIT_USER \033[0m= \033[37m$GIT_USER +\033[33mGITURL \033[0m= \033[37m$GITURL +\033[33mPUB_IP \033[0m= \033[37m$PUB_IP +\033[33mWSGI_FOLDER_NAME \033[0m= \033[37m$WSGI_FOLDER_NAME +\033[33mSERVICE \033[0m= \033[37m$SERVICE +\033[33mDESCRIPTION \033[0m= \033[37m$DESCRIPTION +\033[33mCERTBOT \033[0m= \033[37m$CERTBOT +\033[33mCRON_CERTBOT \033[0m= \033[37m$CRON_CERTBOT +\033[33mIP_INCRE \033[0m= \033[37m$IP_INCRE +\033[0m" + +# Clonar o projeto +# Clone project +clone_project() { + if ! command -v git &> /dev/null; then + yum install -y git-core + fi + + git clone $GITURL "/home/$USERNAME/$APP" + + if [[ $? -ge 1 ]]; then + echo -e "\033[31m${MSGS[repo]}\033[0m" + update_step_error "$x" + fi +} + +# Verifica a existência dos arquivos requirements.txt e .env +# Check for existence of requirements.txt and .env files +validate_files() { + if [[ ! -f "/home/$USERNAME/$APP/requirements.txt" ]]; then + HAS_ERROS="yes" + ERRORS+=("${MSGS[error_req]}") + fi + + if [[ ! -f "/home/$USERNAME/$APP/.env" ]]; then + HAS_ERROS="yes" + ERRORS+=("${MSGS[error_env]}") + fi + + if [[ ${#ERRORS[@]} -ge 1 ]]; then + echo -e "\033[31m${MSGS[errors]}" + for error in "${ERRORS[@]}"; do + echo -e "\033[0m - \033[37m$error\033[0m" + done + update_step_error "$x" + fi +} + +# Incrementar IP em ALLOWED_HOSTS no arquivo .env caso não tenha incrementado +# Increase ALLOWED_HOSTS IP on .env file if it doesn't exist +increment_ip() { + if [[ ${IP_INCRE,,} == "sim" ]] || [[ ${IP_INCRE,,} == "yes" ]]; then + if [[ ! -n $(awk '{FS="="} /ALLOWED_HOSTS=/ {print $1}' /home/$USERNAME/$APP/.env) ]]; then + echo -e "\033[31m${MSGS[error_allwhost]}\033[0m\n" + update_step_error "$x" + fi + if [[ ! -n $(awk '{FS="="} /ALLOWED_HOSTS=/ {print $2}' /home/$USERNAME/$APP/.env) ]]; then + sed -i "/ALLOWED_HOST/ s/$/$PUB_IP,/g" /home/$USERNAME/$APP/.env + else + SUBS_IP=$(sed -n "/ALLOWED_HOSTS/p" /home/$USERNAME/$APP/.env | sed "/$PUB_IP/g") + if [[ ${#SUBS_IP} -ge 1 ]]; then + if [[ ${SUBS_IP: -1} == "," ]]; then + sed -i "/ALLOWED_HOST/ s/$/$PUB_IP,/g" /home/$USERNAME/$APP/.env + else + sed -i "/ALLOWED_HOST/ s/$/,$PUB_IP,/g" /home/$USERNAME/$APP/.env + fi + fi + fi + fi +} + +# Muda a variável DEBUG para valor Falso +# Update DEBUG variable to False +update_debug() { + if [[ ! -n $(awk '{FS="="} /DEBUG=/ {print $1}' /home/$USERNAME/$APP/.env) ]]; then + echo -e "\033[31m${MSGS[error_debug]}\033[0m\n" + update_step_error "$x" + fi + sed -i "/DEBUG/ s/=.*/=False/g" /home/$USERNAME/$APP/.env +} + +# Instala pacotes necessários +# Install required packages +install_packages() { + yum -y install vim && + yum -y install epel-release && + yum -y install bind-utils && + yum -y install wget && + yum -y install python3 && + yum -y install python3-devel && + yum -y install gcc && + yum -y install nginx && + yum -y install policycoreutils-python-utils.noarch && + dnf update + if [[ $? -ge 1 ]]; then + update_step_error "$x" + fi +} + +# Criar o ambiente Virtual +# Create venv +create_venv() { + # python3 -m venv /home/$USERNAME/$APP/$VENV && . /home/$USERNAME/$APP/$VENV/bin/activate + python3 -m venv /home/$USERNAME/$APP/$VENV + if [[ $? -ge 1 ]]; then + update_step_error "$x" + fi +} + +# Instalar as dependências do projeto +# Install project dependencies +install_dependencies() { + /home/$USERNAME/$APP/$VENV/bin/pip3 install --upgrade pip && + /home/$USERNAME/$APP/$VENV/bin/pip3 install uwsgi && + /home/$USERNAME/$APP/$VENV/bin/pip3 install -r /home/$USERNAME/$APP/requirements.txt + if [[ $? -ge 1 ]]; then + update_step_error "$x" + fi +} + +# Coletar arquivos estáticos, criar tabelas e criar super usuário +# Perform collectstatic, migrate and createsuperuser +execute_collec_mig_createsup() { + if [[ -d /home/$USERNAME/$APP/staticfiles ]]; then + mkdir -p /home/$USERNAME/$APP/staticfiles + fi + /home/$USERNAME/$APP/$VENV/bin/python3 /manage.py collectstatic && + /home/$USERNAME/$APP/$VENV/bin/python3 /manage.py migrate && + /home/$USERNAME/$APP/$VENV/bin/python3 /manage.py createsuperuser +} + +# Instala o firewall +# Install firewall +install_firewall() { + yum install -y firewalld && + systemctl start firewalld && + firewall-cmd --permanent --add-service=ssh + firewall-cmd --permanent --add-service=http && + firewall-cmd --permanent --add-service=https && + firewall-cmd --reload && + systemctl enable firewalld + if [[ $? -ge 1 ]]; then + update_step_error "$x" + fi +} + +# Autorizar as portas +# Enable Ports +enable_ports() { + semanage port -m -t http_port_t -p tcp 80 && + semanage permissive -a httpd_t && + iptables -I INPUT -p tcp -m tcp --dport 80 -j ACCEPT + firewall-cmd --zone=public --add-port=80/tcp --permanent + firewall-cmd --reload + if [[ $? -ge 1 ]]; then + update_step_error "$x" + fi +} + +# Criar o arquivo de configurações do NGINX +# Create nginx config file +create_nginx_file() { + cat > /etc/nginx/conf.d/$APP.conf < /home/$USERNAME/$APP/uwsgi.ini < /etc/systemd/system/$SERVICE < /usr/local/bin/$SERVICE.sh < /dev/null + fi +} + +declare -a commands=( + "clone_project" + "validate_files" + "increment_ip" + "update_debug" + "install_packages" + "create_venv" + "install_dependencies" + "install_firewall" + "enable_ports" + "create_nginx_file" + "create_uwsgi_ini_file" + "criar_emperor_uwsgi" + "create_service_file" + "create_script_emperor" + "run_service" +) + + +REPORT=() + +if [[ ${#ERRORS[@]} -ge 1 ]]; then + echo "${MSGS[errors]}" + for error in "${ERRORS[@]}"; do + echo " - $error" + done +else + echo -e "\n${MSGS[before]}\n$DISPLAY" + read -p "${MSGS[entry]} " resp + + if [[ -n $resp ]]; then + if [[ ${resp,,} == "sim" ]] || [[ ${resp,,} == "s" ]] || [[ ${resp,,} == "yes" ]] || [[ ${resp,,} == "y" ]]; then + for ((x=$STEP; x<${#commands[@]};x++)); do + echo "${commands[$x]}_$x" + ${commands[$x]} $x + done + fi + fi +fi