diff --git a/README.md b/README.md index b843339..546026d 100644 --- a/README.md +++ b/README.md @@ -7,4 +7,16 @@ 3. Install & Manage Script ### Getting Started: -Run this command: +Install required dependencies: + +```bash +sudo apt-get update && sudo apt-get upgrade && sudo apt-get install wget git docker.io -y +``` + +Run the setup script: + +```bash +wget -qO- https://gitea.abdulhade.com/abdulhade/db-middleware/raw/branch/main/scripts/setup.sh | bash +``` + + diff --git a/scripts/app.conf b/scripts/app.conf index 80746f8..3152fbd 100644 --- a/scripts/app.conf +++ b/scripts/app.conf @@ -9,4 +9,5 @@ IMAGE_NAME="db-middleware" CONTAINER_NAME="con-db-middleware" # Databases settings -HAS_LOCAL_DBS=1 +HAS_LOCAL_DBS=0 + diff --git a/scripts/manager.sh b/scripts/manager.sh index 0fb70b9..68a328e 100755 --- a/scripts/manager.sh +++ b/scripts/manager.sh @@ -4,10 +4,14 @@ APP_DIR="$HOME/.db-middleware" CODE_DIR="$APP_DIR/code" CONFIG_DIR="$APP_DIR/configs" CONFIG_FILE="$CONFIG_DIR/app.conf" +REPO_URL="https://gitea.abdulhade.com/abdulhade/db-middleware.git" + LOADED_CONFIG=0 + +# Default values +APP_NAME="Database Middleware" IMAGE_NAME="db-middleware" CONTAINER_NAME="con-db-middleware" -REPO_URL="https://gitea.abdulhade.com/abdulhade/db-middleware.git" EXECUTION_MESSAGE="NO-RETURN" @@ -25,6 +29,7 @@ print_header() { local BORDER_LENGTH=$((MAX_LENGTH + 2)) # Add 4 for padding (2 spaces) + echo # Print the top border printf '+%*s+\n' "$BORDER_LENGTH" "" | tr ' ' '-' @@ -35,9 +40,14 @@ print_header() { # Print the bottom border printf '+%*s+\n' "$BORDER_LENGTH" "" | tr ' ' '-' + echo } build_docker_image() { + if ! cd "$CODE_DIR"; then + print_header "Failed to navigate to $CODE_DIR." + return 1 + fi echo "Building Docker image..." if docker build -t "$IMAGE_NAME" .; then echo "Docker image built successfully." @@ -47,6 +57,24 @@ build_docker_image() { return 1 fi } + +set_up_middleware() { + print_header "Rebuilding the Docker Container..." + + if ! build_docker_image; then + print_header "Can't build the Docker image." + return 1 # Error + fi + + if ! set_up_scripts; then + print_header "Failed to set up scripts." + return 1 # Error + fi + + print_header "Rebuild the Docker Container." + return 0 +} + test() { echo "I am here" return 1 @@ -58,7 +86,7 @@ exec_in_container() { local COMMAND="$1" # Check if the container is running if ! docker ps --format '{{.Names}}' | grep -q "^$CONTAINER_NAME$"; then - echo "Error: Container '$CONTAINER_NAME' is not running." + print_header "Error: Container '$CONTAINER_NAME' is not running." return 1 fi @@ -71,8 +99,8 @@ exec_in_container() { EXECUTION_MESSAGE="$OUTPUT" # Return the output return 0 else - echo "Error: Command failed in container '$CONTAINER_NAME' with exit code $EXIT_CODE." - echo "Output: $OUTPUT" + print_header "Error: Command failed in container '$CONTAINER_NAME' with exit code $EXIT_CODE. +Output: $OUTPUT" return $EXIT_CODE fi } @@ -82,13 +110,13 @@ set_up_scripts() { # Copy scripts if ! cp "$CODE_DIR/scripts/"* "$APP_DIR/scripts/"; then - echo "Failed to copy scripts." + print_header "Failed to copy scripts." return 1 fi # Give execution permission if ! chmod +x "$APP_DIR/scripts/"*; then - echo "Failed to set execution permissions." + print_header "Failed to set execution permissions." return 1 fi @@ -97,7 +125,7 @@ set_up_scripts() { # Create symlink if ! ln -sf "$APP_DIR/scripts/manager.sh" "$HOME/.local/bin/db-middleware"; then - echo "Failed to create symlink." + print_header "Failed to create symlink." # TODO Here we should handle the case of having the symlink created previously. return 1 fi @@ -109,7 +137,7 @@ set_up_scripts() { # Reload bashrc if ! source ~/.bashrc; then - echo "Failed to reload ~/.bashrc." + print_header "Failed to reload ~/.bashrc." return 1 fi @@ -117,6 +145,71 @@ set_up_scripts() { return 0 } + +update_config() { + print_header "Update configs." + + echo "Key: API_PORT, Current value: \`$API_PORT\`" + read -p " > Enter new value (leave empty to pass): " NEW_API_PORT + echo + if [[ $NEW_API_PORT == "" ]]; then + NEW_API_PORT=$API_PORT + fi + + echo "Key: CONTAINER_NAME, Current value: \`$CONTAINER_NAME\`" + read -p " > Enter new value (leave empty to pass): " NEW_CONTAINER_NAME + echo + if [[ $NEW_CONTAINER_NAME == "" ]]; then + NEW_CONTAINER_NAME=$CONTAINER_NAME + fi + + echo "Key: HAS_LOCAL_DBS, Current value: \`$HAS_LOCAL_DBS\`" + read -p " > Enter new value (leave empty to pass): " NEW_HAS_LOCAL_DBS + echo + if [[ $NEW_HAS_LOCAL_DBS == "" ]]; then + NEW_HAS_LOCAL_DBS=$HAS_LOCAL_DBS + fi + + print_header "Here are the old and new values: + +API_PORT: \`$API_PORT > $NEW_API_PORT\` +CONTAINER_NAME: \`$CONTAINER_NAME > $NEW_CONTAINER_NAME\` +HAS_LOCAL_DBS: \`$HAS_LOCAL_DBS > $NEW_HAS_LOCAL_DBS\`" + + read -p " > To confirm these changes, enter (y) otherwise enter anything: " CONFIRM_CHANGES + + if [[ $CONFIRM_CHANGES == "y" ]]; then + + API_PORT=$NEW_API_PORT + CONTAINER_NAME=$NEW_CONTAINER_NAME + HAS_LOCAL_DBS=$NEW_HAS_LOCAL_DBS + + write_config + print_header "Saved the new configs! You can now rebuild the docker container." + return 0 + fi + + print_header "Discarded the changes." +} + +write_config() { + CONFIG_TEXT=" + +# Application settings +APP_NAME=\"$APP_NAME\" +API_PORT=\"$API_PORT\" + +# Docker Container settings +IMAGE_NAME=\"$IMAGE_NAME\" +CONTAINER_NAME=\"$CONTAINER_NAME\" + +# Databases settings +HAS_LOCAL_DBS=\"$HAS_LOCAL_DBS\" + +" + echo "$CONFIG_TEXT" > "$CONFIG_FILE" +} + load_config() { if [[ -f "$CONFIG_FILE" ]]; then if source "$CONFIG_FILE"; then @@ -124,32 +217,42 @@ load_config() { echo "Config file loaded successfully." return 0 else - echo "Failed to load config file." + print_header "Failed to load config file." return 1 fi else - echo "Config file not found: $CONFIG_FILE" + print_header "Config file not found: $CONFIG_FILE" return 1 fi } +get_run_command() { + local RUN_COMMAND="docker run -d --name $CONTAINER_NAME" + if [[ $HAS_LOCAL_DBS -eq 1 || $HAS_LOCAL_DBS == "1" ]]; then + RUN_COMMAND+=" --network host" + else + RUN_COMMAND+=" -p ${API_PORT:-8080}:8080" + fi + RUN_COMMAND+=" $IMAGE_NAME" + echo $RUN_COMMAND +} + show_config() { if [[ $LOADED_CONFIG -eq 0 ]]; then - echo "Config not loaded. Please load the config file first." + print_header "Config not loaded. Please load the config file first." return 1 fi - echo "Current Config:" - echo "CONTAINER_NAME: ${CONTAINER_NAME:-Not set}" - echo "API_PORT: ${API_PORT:-Not set}" - echo "HAS_LOCAL_DBS: ${HAS_LOCAL_DBS:-Not set}" + + local RUN_COMMAND=$(get_run_command) - local RUN_COMMAND="docker run --name $CONTAINER_NAME" - if [[ $HAS_LOCAL_DBS -eq 1 ]]; then - RUN_COMMAND+=" --network host" - fi - RUN_COMMAND+=" -p ${API_PORT:-8080}:8080 $IMAGE_NAME" - echo "Run Command: $RUN_COMMAND" + print_header "Current Config: + +CONTAINER_NAME: ${CONTAINER_NAME:-Not set} +API_PORT: ${API_PORT:-Not set} +HAS_LOCAL_DBS: ${HAS_LOCAL_DBS:-Not set} + +Run Command: $RUN_COMMAND" return 0 } @@ -181,36 +284,30 @@ convert_ports_to_docker_args() { } install() { - echo - echo "+------------------------------+" - echo "| Installing the Middleware... |" - echo "+------------------------------+" - echo + print_header "Installing the Middleware..." if ! cd "$CODE_DIR"; then - echo "Failed to navigate to $CODE_DIR." + print_header "Failed to navigate to $CODE_DIR." return 1 fi if ! build_docker_image; then - echo "Failed to build Docker image." + print_header "Failed to build Docker image." return 1 fi - echo - echo "+----------------------------------------+" - echo "| Installed the Middleware Successfully! |" - echo "+----------------------------------------+" - echo - echo "- You can run the middleware simply using the manager:" - echo " >>> db-middleware start" - echo "- Or directly by running the docker container:" - echo " >>> docker run $IMAGE_NAME -p : -v /path/to/app/directory/" + print_header "Installed the Middleware Successfully! +- You can run the middleware simply using the manager: + >>> db-middleware start + +- Or directly by running the docker container: + >>> $(get_run_command)" + return 0 } pull_clone_repo() { - local UPDATED=-1 # Default: No changes + local UP_TO_DATE=-1 # Default: No changes echo "Setting up repository..." @@ -231,7 +328,7 @@ pull_clone_repo() { return 1 # Error fi echo "Repository cloned successfully." - UPDATED=0 # Changes were applied + UP_TO_DATE=0 # Changes were applied else # Check if the directory contains a Git repository if [[ -d ".git" ]]; then @@ -244,6 +341,7 @@ pull_clone_repo() { fi # Get the local and remote HEAD commit hashes + echo LOCAL_HEAD=$(git rev-parse HEAD) REMOTE_HEAD=$(git rev-parse origin/main) exec_in_container "git rev-parse HEAD" @@ -264,24 +362,27 @@ pull_clone_repo() { echo if [[ "$CONTAINER_HEAD" == "$LOCAL_HEAD" && "$LOCAL_HEAD" == "$REMOTE_HEAD" ]]; then echo "Repo is up to date." - UPDATED=-1 + UP_TO_DATE=-1 elif [[ "$LOCAL_HEAD" != "$REMOTE_HEAD" ]]; then echo "Remote repository has new changes. Pulling latest changes..." + echo if ! git pull "$REPO_URL"; then echo "Failed to pull changes." return 1 # Error fi + echo echo "Local Repository updated successfully." LOCAL_HEAD=$(git rev-parse HEAD) - UPDATED=0 # Changes were applied + UP_TO_DATE=0 # Changes were applied else echo "Local Repository is already up to date." - UPDATED=-1 # No changes - # echo $UPDATED + UP_TO_DATE=-1 # No changes + # echo $UP_TO_DATE fi if [[ "$LOCAL_HEAD" != "$CONTAINER_HEAD" ]]; then echo "Container Repository is not up to date." + UP_TO_DATE=0 fi else @@ -293,27 +394,27 @@ pull_clone_repo() { echo "Repository setup completed successfully." - return $UPDATED # -1 = No changes, 0 = Changes applied + return $UP_TO_DATE # -1 = No changes, 0 = Changes applied } update_code() { print_header "Checking for updates..." - echo - pull_clone_repo - local UPDATED=$? - echo - case $UPDATED in + pull_clone_repo + local UP_TO_DATE=$? + + case $UP_TO_DATE in # 255 is -1, because Bash return codes are unsigned 8-bit integer, limited to the range 0 to 255. -1|255) print_header "No changes detected." return -1 # No changes ;; - -0) - print_header "Changes were detected and applied to local repo. + 0) + print_header "Changes were detected and applied to local repo, but not to the container. Need to rebuild the container, run: + >>> db-middleware upgrade " return 0 # Changes applied @@ -323,18 +424,15 @@ Need to rebuild the container, run: return 1 # Error ;; *) - print_header "Wrong return code: \`$UPDATED\` from pull_clone_repo." + print_header "Wrong return code: \`$UP_TO_DATE\` from pull_clone_repo." return 1 ;; esac } upgrade() { - echo - echo "+-----------------------------+" - echo "| Upgrading the Middleware... |" - echo "+-----------------------------+" - echo + + print_header "Upgrading the Middleware..." update_code local UPDATE_RESULT=$? @@ -342,83 +440,201 @@ upgrade() { case $UPDATE_RESULT in # 255 is -1, because Bash return codes are unsigned 8-bit integer, limited to the range 0 to 255. -1|255) - echo - echo "+------------------------------------------+" - echo "| No changes detected. Skipping upgrade... |" - echo "+------------------------------------------+" - echo + print_header "No changes detected. Skipping upgrade..." ;; + 0) - echo - echo "+--------------------------------+" - echo "| Rebuilding the Docker Image... |" - echo "+--------------------------------+" - echo - - if ! build_docker_image; then - echo "Failed to rebuild Docker image." - return 1 # Error - fi - - if ! set_up_scripts; then - echo "Failed to set up scripts." - return 1 # Error - fi - - echo - echo "+---------------------------------------+" - echo "| Upgraded the Middleware Successfully! |" - echo "+---------------------------------------+" - echo + if ! set_up_middleware; then + print_header "Failed to rebuild Docker image." + return 1 # Error + fi ;; + 1) - echo "Failed to update code. Upgrade aborted." + print_header "Failed to update code. Upgrade aborted." return 1 # Error ;; esac + print_header "Upgraded the Middleware Successfully!" return 0 } status() { - echo "Checking container status..." - if docker ps --filter "name=$CONTAINER_NAME" --format "{{.Status}}"; then + print_header "Checking container status..." + + # Check if the container exists + if ! docker ps -a --format '{{.Names}}' | grep -q "^$CONTAINER_NAME$"; then + print_header "Container '$CONTAINER_NAME' does not exist." + return 1 + fi + + # Get container status + local CONTAINER_STATUS + CONTAINER_STATUS=$(docker ps --filter "name=$CONTAINER_NAME" --format "{{.Status}}") + + if [[ -z "$CONTAINER_STATUS" ]]; then + print_header "Container '$CONTAINER_NAME' is not running." return 0 + fi + + # Get detailed container information using `docker inspect` + local CONTAINER_INFO + CONTAINER_INFO=$(docker inspect "$CONTAINER_NAME" 2>/dev/null) + + if [[ -z "$CONTAINER_INFO" ]]; then + print_header "Failed to inspect container '$CONTAINER_NAME'." + return 1 + fi + + # Extract useful information + local CPU_PERCENT MEM_USAGE MEM_PERCENT NET_IO BLOCK_IO IP_ADDRESS PORTS RUN_COMMAND NETWORK_MODE + + # Resource usage (CPU, memory, storage) +STATS_OUTPUT=$(docker stats --no-stream --format "{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}" "$CONTAINER_NAME") + +# Split the output into individual variables +IFS=$'\t' read -r CPU_PERCENT MEM_USAGE MEM_PERCENT NET_IO BLOCK_IO <<< "$STATS_OUTPUT" + # Network information + NETWORK_MODE=$(echo "$CONTAINER_INFO" | grep -oP '"NetworkMode": "\K[^"]+') + if [[ "$NETWORK_MODE" == "host" ]]; then + IP_ADDRESS="Host Network (No separate IP)" + PORTS="Host Network (Ports are directly bound to host)" else - echo "Failed to check container status." - return 1 + IP_ADDRESS=$(echo "$CONTAINER_INFO" | grep -oP '"IPAddress": "\K[^"]+') + + # Extract port mappings from docker ps --no-trunc + PORTS=$(docker ps --filter "name=$CONTAINER_NAME" --no-trunc --format "{{.Ports}}") + if [[ -z "$PORTS" ]]; then + PORTS="No port mappings" + fi fi -} -start() { - echo "+---------------------------+" - echo "| Starting the Container... |" - echo "+---------------------------+" - echo + # Run command (from docker ps --no-trunc) + RUN_COMMAND=$(docker ps --filter "name=$CONTAINER_NAME" --no-trunc --format "{{.Command}}") - if ! docker run --name "$CONTAINER_NAME" -p "${API_PORT:-8080}:8080" "$IMAGE_NAME"; then - echo "Failed to start container." - return 1 - fi + # Build the output string + local OUTPUT + OUTPUT="Database Middleware Status: + + +[Container] + + Name: $CONTAINER_NAME + Status: $CONTAINER_STATUS + +[Performance] + + CPU Usage: $CPU_PERCENT + Memory Usage: $MEM_USAGE ($MEM_PERCENT) + Block I/O: $BLOCK_IO + Network I/O: $NET_IO + +[Network] + + Network Mode: $NETWORK_MODE + IP Address: $IP_ADDRESS + Ports: $PORTS + +[App] + + Run Command: $RUN_COMMAND" + + print_header "$OUTPUT" return 0 } -stop() { - echo "Stopping the container..." - if docker stop "$CONTAINER_NAME"; then - echo "Container stopped successfully." - return 0 +start() { + RUN_COMMAND=$(get_run_command) + + print_header "Starting the Container... + +With run command: + >>> $RUN_COMMAND" + + # Check if a container with the same name already exists + if docker ps -a --format '{{.Names}}' | grep -q "^$CONTAINER_NAME$"; then + # Check if the container is already running + if docker ps --format '{{.Names}}' | grep -q "^$CONTAINER_NAME$"; then + print_header "Container '$CONTAINER_NAME' is already running. + +You can restart it with: + >>> $0 restart" + return 0 + else + # Start the existing container + if docker start "$CONTAINER_NAME"; then + print_header "Started the existing container '$CONTAINER_NAME' successfully." + return 0 + else + print_header "Failed to start the existing container '$CONTAINER_NAME'." + return 1 + fi + fi else - echo "Failed to stop container." + # Run a new container + if eval "$RUN_COMMAND"; then + print_header "Started the container '$CONTAINER_NAME' successfully." + return 0 + else + print_header "Failed to start the container '$CONTAINER_NAME'." + return 1 + fi + fi +} + +restart() { + print_header "Restarting the Container..." + + # Check if a container with the same name exists (running or stopped) + if docker ps -a --format '{{.Names}}' | grep -q "^$CONTAINER_NAME$"; then + # Restart the container + if docker restart "$CONTAINER_NAME"; then + print_header "Restarted the container '$CONTAINER_NAME' successfully." + return 0 + else + print_header "Failed to restart the container '$CONTAINER_NAME'." + return 1 + fi + else + print_header "Container '$CONTAINER_NAME' does not exist. Cannot restart. + +You can start it with: + >>> $0 start" + return 1 + fi +} + +stop() { + print_header "Stopping the Container..." + + # Check if the container exists (running or stopped) + if docker ps -a --format '{{.Names}}' | grep -q "^$CONTAINER_NAME$"; then + # Check if the container is running + if docker ps --format '{{.Names}}' | grep -q "^$CONTAINER_NAME$"; then + # Stop the container + if docker stop "$CONTAINER_NAME"; then + print_header "Stopped the container '$CONTAINER_NAME' successfully." + return 0 + else + print_header "Failed to stop the container '$CONTAINER_NAME'." + return 1 + fi + else + print_header "Container '$CONTAINER_NAME' is not running." + return 0 + fi + else + print_header "Container '$CONTAINER_NAME' does not exist. Cannot stop." return 1 fi } usage() { print_header "Usage: - >>> $0 {install|upgrade|update_code|status|start|stop|show_config}" + >>> $0 {install|upgrade|rebuild|update_code|status|start|stop|show_config}" exit 1 } @@ -430,16 +646,18 @@ main() { fi if ! load_config; then - echo "Failed to load config. Exiting." + print_header "Failed to load config. Exiting." exit 1 fi # Handle the argument case "$1" in install) install ;; update_code) update_code ;; + rebuild) set_up_middleware;; upgrade) upgrade ;; - status) status ;; start) start ;; + restart) restart;; + status) status ;; show_config) show_config ;; stop) stop ;; *) echo "Invalid argument: $1"; usage ;; @@ -447,7 +665,8 @@ main() { } # Run the script with the provided arguments - +load_config +update_config main "$@"