#!/usr/bin/env bash
#
# Copyright (c) 2023-2026, Cloudera, Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

SCRIPT_PATH="$(
  cd "$(dirname "$0")" >/dev/null 2>&1 || exit
  pwd -P
)"

CMA_APP_DATA_DIR=${CMA_APP_DATA_DIR:-${HOME}/.cma}

CMA_DIST_TYPE=${CMA_DIST_TYPE:-distributed}
export CMA_DIST_TYPE
export CMA_STACK_NAME=${CMA_STACK_NAME:-cma}

DOCKER_OS=${DOCKER_OS:-rhel}
DOCKER_HOST_PORT=${DOCKER_HOST_PORT:-8090}
CMA_AGENT_DOCKER_HOST_PORT=${CMA_AGENT_DOCKER_HOST_PORT:-9090}
CMA_DOCKER_STARTUP_TIMEOUT=${CMA_DOCKER_STARTUP_TIMEOUT:-300}
PYPI_WEBSERVER_PORT=${PYPI_WEBSERVER_PORT:-9003}
DOCKER_CONTAINER_NAME=${DOCKER_CONTAINER_NAME:-cma-server}
DOCKER_EXTRA_ARGS=

AIRGAPPED=${AIRGAPPED:-false}



CMA_STANDALONE_DIST_TYPE="standalone"
CMA_DISTRIBUTED_DIST_TYPE="distributed"
CMA_AGENT_DIST_TYPE="agent"

CMA_CONTAINER_INSTANCE_DIR=${CMA_CONTAINER_INSTANCE_DIR:-/home/cma/cma-server}
CMA_CONTAINER_CONF_DIR=${CMA_CONTAINER_INSTANCE_DIR}/conf
CMA_CONTAINER_DATA_DIR=${CMA_CONTAINER_INSTANCE_DIR}/data
CMA_CONTAINER_LOGS_DIR=${CMA_CONTAINER_INSTANCE_DIR}/logs
CMA_CONTAINER_SSL_DIR=${CMA_CONTAINER_INSTANCE_DIR}/ssl

CMA_HOST_INSTANCE_DIR=${CMA_APP_DATA_DIR}/${DOCKER_CONTAINER_NAME}
CMA_HOST_CONF_DIR=${CMA_HOST_CONF_DIR:-${CMA_HOST_INSTANCE_DIR}/conf}
CMA_HOST_DATA_DIR=${CMA_HOST_DATA_DIR:-${CMA_HOST_INSTANCE_DIR}/data}
CMA_HOST_LOGS_DIR=${CMA_HOST_LOGS_DIR:-${CMA_HOST_INSTANCE_DIR}/logs}
CMA_HOST_SSL_DIR=${CMA_HOST_SSL_DIR:-${CMA_HOST_INSTANCE_DIR}/ssl}

echo "Creating host dirs.."
mkdir -p $CMA_HOST_CONF_DIR
mkdir -p $CMA_HOST_DATA_DIR
mkdir -p $CMA_HOST_LOGS_DIR
mkdir -p $CMA_HOST_SSL_DIR

CMA_CONF_DIR=${CMA_HOST_CONF_DIR}
export CMA_LOG_DIR=${CMA_HOST_LOGS_DIR}
source "$SCRIPT_PATH"/common.sh # load commons
create_logs_dir

LOG_FILE="$LOGS_DIR"/$(basename "$0" .sh).log
ENV_FILE="$CMA_HOST_CONF_DIR"/$(basename "$0" .sh).env

load_env_file "${ENV_FILE}"

# set all defaults after env file loading
CMA_DOCKER_IMAGE_VERSION="${CMA_DOCKER_IMAGE_VERSION:-${DEFAULT_CMA_DOCKER_IMAGE_VERSION:-latest}}"
SKIP_PORT_PUBLISHING=${SKIP_PORT_PUBLISHING:-false}
CUSTOM_DOCKER_IP_SETTINGS=${CUSTOM_DOCKER_IP_SETTINGS:-false}

CMA_EXTRAS_GPL_TAR_LOCATION=${CMA_EXTRAS_GPL_TAR_LOCATION:-$(ls "${ROOT_DIR}/.."/cma-extras-gpl-*.tar.gz 2>/dev/null | tail -n1)}

if [ -z "${ACTION_START}" ] && [ -z "${ACTION_STOP}" ] && [ -z "${ACTION_REBUILD}" ] && [ -z "${RUN_SSL_SETUP}" ]; then
  ACTION_START=true
fi

echo "#################################################################################################################" >>"$LOG_FILE"
echo "Actions: ACTION_START: ${ACTION_START:-false}, ACTION_STOP: ${ACTION_STOP:-false}, ACTION_REBUILD: ${ACTION_REBUILD:-false}, RUN_SSL_SETUP: ${RUN_SSL_SETUP:-false}" >>"$LOG_FILE"

if ! docker info >/dev/null 2>&1; then
  echo "This script uses docker, and it isn't running - please start docker and try again!" | tee -a "$LOG_FILE"
  exit 1
fi

if [ ${CMA_DIST_TYPE} = "distributed" ]; then
  if ! docker compose version >/dev/null 2>&1; then
    echo "This script uses docker compose, and it isn't installed - please install docker compose and try again!" | tee -a "$LOG_FILE"
    exit 1
  fi
fi

if [ "${CMA_MASTER}" = true ]; then
  export CMA_SERVER_DOCKERFILE="cma-master.dockerfile"
  CMA_DOCKER_IMAGE_BASE="cma-master-server"
fi

if [ -n "${CMA_SERVER_CONF_FILE}" ]; then
  if [ -f "${CMA_SERVER_CONF_FILE}" ]; then

    echo "Loading cma-server-conf file (${CMA_SERVER_CONF_FILE})"

    if [ -z "${CMA_SERVER_SSL}" ]; then
      if is_ssl_active; then
        CMA_SERVER_SSL=true
      fi
    fi
    if [ -z "${CMA_VAULT}" ]; then
      if is_vault_active; then
        CMA_VAULT=true
      fi
    fi

    if [ -z "${CMA_SERVER_EXTRA_PROFILE}" ]; then
      CMA_SERVER_EXTRA_PROFILE=$(get_extra_profiles)
    fi
  fi
fi

if [ -n "${CMA_SSL_CONF_FILE}" ] && [ -f "${CMA_SSL_CONF_FILE}" ]; then

  echo "Loading cma-ssl file (${CMA_SSL_CONF_FILE})"
  if [ -z "${CMA_SERVER_SSL}" ]; then
    if is_ssl_active; then
      CMA_SERVER_SSL=true
    fi
  fi
fi

echo "SSL configured: ${CMA_SERVER_SSL:-false}" | tee -a "$LOG_FILE"
echo "Vault configured: ${CMA_VAULT:-false}" | tee -a "$LOG_FILE"
echo "Airgapped configured: ${AIRGAPPED:-false}" | tee -a "$LOG_FILE"

if [ -n "${CMA_SERVER_EXTRA_PROFILE}" ]; then
  echo "Additional profile(s): ${CMA_SERVER_EXTRA_PROFILE}" | tee -a "$LOG_FILE"
fi

CMA_DOCKER_IMAGE_BASE=${CMA_DOCKER_IMAGE_BASE:-"cma-${DOCKER_OS}"}
CMA_DOCKER_IMAGE=${CMA_DOCKER_IMAGE_BASE}:${CMA_DOCKER_IMAGE_VERSION}
CMA_AGENT_DOCKER_IMAGE_BASE=${CMA_AGENT_DOCKER_IMAGE_BASE:-"cma-agent-${DOCKER_OS}"}
CMA_AGENT_DOCKER_IMAGE=${CMA_AGENT_DOCKER_IMAGE_BASE}:${CMA_DOCKER_IMAGE_VERSION}

stop_container() {
  if [ "$(docker ps -q -f name="${DOCKER_CONTAINER_NAME}")" ]; then
    echo "Stopping the ${DOCKER_CONTAINER_NAME} container... " | tee -a "${LOG_FILE}"
    docker stop "${DOCKER_CONTAINER_NAME}" >>"${LOG_FILE}" 2>&1

    if docker ps | grep "${DOCKER_CONTAINER_NAME}" >/dev/null 2>&1; then
      echo "Failed." | tee -a "$LOG_FILE"
      exit 1
    else
      echo "Removing ${ROOT_DIR}/cma-server/cma-server.pid" | tee -a "$LOG_FILE"
      rm -f "${ROOT_DIR}/cma-server/cma-server.pid"
      echo "Done." | tee -a "$LOG_FILE"
    fi
  fi
}

delete_container() {
  if docker container inspect "${DOCKER_CONTAINER_NAME}" >/dev/null 2>&1; then

    echo -n "Deleting the ${DOCKER_CONTAINER_NAME} container... " | tee -a "$LOG_FILE"
    docker rm "${DOCKER_CONTAINER_NAME}" >>"$LOG_FILE" 2>&1

    if ! docker container inspect "${DOCKER_CONTAINER_NAME}" >/dev/null 2>&1; then
      echo "Done." | tee -a "$LOG_FILE"
    else
      echo "Failed." | tee -a "$LOG_FILE"
      exit 1
    fi
  fi
}

delete_image() {
  if docker image inspect "${CMA_DOCKER_IMAGE}" >/dev/null 2>&1; then

    echo -n "Deleting the ${CMA_DOCKER_IMAGE} image... " | tee -a "$LOG_FILE"
    docker rmi "${CMA_DOCKER_IMAGE}" >>"$LOG_FILE" 2>&1

    if ! docker image inspect "${CMA_DOCKER_IMAGE}" >/dev/null 2>&1; then
      echo "Done." | tee -a "$LOG_FILE"
    else
      echo "Failed." | tee -a "$LOG_FILE"
      exit 1
    fi
  fi
}

build_image() {
  local dist_type="${1:-${CMA_DISTRIBUTED_DIST_TYPE}}"

  if ! docker image inspect "${CMA_DOCKER_IMAGE}" >/dev/null 2>&1; then
    echo "The ${CMA_DOCKER_IMAGE} image is missing!" | tee -a "$LOG_FILE"
    CMA_SETUP_OPTS="--docker -os=${DOCKER_OS} -iv=${CMA_DOCKER_IMAGE_VERSION} --profile=${CMA_SERVER_EXTRA_PROFILE} -d=${dist_type} "
    if [ "$AIRGAPPED" = true ]; then
      CMA_SETUP_OPTS+="--cma-extras-gpl-tar-location=${CMA_EXTRAS_GPL_TAR_LOCATION} --pypi-webserver-port=${PYPI_WEBSERVER_PORT} --airgapped "
    fi
    if [ "$CMA_SERVER_SSL" = true ]; then
      CMA_SETUP_OPTS+="--ssl "
    fi
    if [ "$CMA_SERVER_LDAP" = true ]; then
      CMA_SETUP_OPTS+="--ldap "
    fi
    if [ "$CMA_VAULT" = true ]; then
      CMA_SETUP_OPTS+="--vault "
    fi

    "$SCRIPT_PATH"/setup.sh ${CMA_SETUP_OPTS}

    if [ $? -ne 0 ]; then
      exit 1
    fi

    NEW_CMA_IMAGE_CREATED=true
    LAST_CMA_DOCKER_IMAGE="${CMA_DOCKER_IMAGE}"
  fi
}


build_agent_image() {
  if ! docker image inspect "${CMA_AGENT_DOCKER_IMAGE}" >/dev/null 2>&1; then
    echo "The ${CMA_AGENT_DOCKER_IMAGE} image is missing!" | tee -a "$LOG_FILE"
    CMA_SETUP_OPTS="--docker -os=${DOCKER_OS} -iv=${CMA_DOCKER_IMAGE_VERSION} --profile=${CMA_SERVER_EXTRA_PROFILE} -d=${CMA_AGENT_DIST_TYPE} "
    if [ "$AIRGAPPED" = true ]; then
      CMA_SETUP_OPTS+="--cma-extras-gpl-tar-location=${CMA_EXTRAS_GPL_TAR_LOCATION} --pypi-webserver-port=${PYPI_WEBSERVER_PORT} --airgapped "
    fi
    if [ "$CMA_SERVER_SSL" = true ]; then
      CMA_SETUP_OPTS+="--ssl "
    fi
    if [ "$CMA_SERVER_LDAP" = true ]; then
      CMA_SETUP_OPTS+="--ldap "
    fi
    if [ "$CMA_VAULT" = true ]; then
      CMA_SETUP_OPTS+="--vault "
    fi

    "$SCRIPT_PATH"/setup.sh ${CMA_SETUP_OPTS}

    if [ $? -ne 0 ]; then
      exit 1
    fi
  fi
}

rebuild_image_standalone() {
  if docker image inspect "${CMA_DOCKER_IMAGE}" >/dev/null 2>&1; then
    stop_container
    delete_container
    delete_image
  fi
  build_image "${CMA_STANDALONE_DIST_TYPE}"
}

rebuild_image_distributed() {
  if docker image inspect "${CMA_DOCKER_IMAGE}" >/dev/null 2>&1; then
    stop_distributed
    delete_image
  fi
  build_image "${CMA_DISTRIBUTED_DIST_TYPE}"
}

rebuild_image_master() {
  rebuild_image_distributed
}

rebuild_image_server() {
  rebuild_image_distributed
}

rebuild_image_agent() {
  if docker image inspect "${CMA_DOCKER_IMAGE}" >/dev/null 2>&1; then
    stop_distributed
    delete_image
  fi
  build_image "${CMA_AGENT_DIST_TYPE}"
}

rebuild_image() {
  if [ -n "${CMA_SERVICE_TYPE}" ]; then
    "rebuild_image_${CMA_SERVICE_TYPE}"
  else
    "rebuild_image_${CMA_DIST_TYPE}"
  fi
}

# get container ID and store in LAST_DOCKER_CONTAINER_ID variable
get_container_id() {
  docker ps -aq -f name=${DOCKER_CONTAINER_NAME}
}

run_container() {

  if [ "$CMA_SKIP_HOST_PORT_CHECK" = true ]; then
    echo "Host port verification skipped!" | tee -a "$LOG_FILE"
  elif [ "${DOCKER_HOST_PORT}" -gt "0" ]; then
    if ! verify_port "${DOCKER_HOST_PORT}"; then
      echo "Host port $DOCKER_HOST_PORT is being used by another process!" | tee -a "$LOG_FILE"
      exit 1
    fi
    echo "Host port verification done!" | tee -a "$LOG_FILE"
  fi

  echo -n "Starting the ${DOCKER_CONTAINER_NAME} container... " | tee -a "$LOG_FILE"
  AM2CM_ROOT_DIR=/$(basename "${ROOT_DIR}")

  if [ -n "${DOCKER_CONTAINER_ENV_FILE}" ]; then
    DOCKER_EXTRA_ARGS+="--env-file ${DOCKER_CONTAINER_ENV_FILE} "
  fi

  if [ -n "${DOCKER_USER}" ]; then
    DOCKER_EXTRA_ARGS+="--user ${DOCKER_USER} "
  fi

  if [ -n "${CUSTOM_DOCKER_NETWORK}" ]; then
    DOCKER_EXTRA_ARGS+="--network ${CUSTOM_DOCKER_NETWORK} "
    export CMA_NETWORK_NAME=${CUSTOM_DOCKER_NETWORK}
  fi

  if [ -z "${CMA_SERVER_PORT}" ]; then
    CMA_SERVER_PORT=$(get_cma_server_port)
  fi

  DOCKER_PORT_ARGS=""
  # If a negative number, start without port publishing at all
  if [ "${DOCKER_HOST_PORT}" -gt "-1" ]; then
    if [ "${DOCKER_HOST_PORT}" == "0" ]; then
      # Random port will be selected
      DOCKER_PORT_ARGS+="-p ${CMA_SERVER_PORT}"
    else
      # Normal port publishing
      DOCKER_PORT_ARGS+="-p ${DOCKER_HOST_PORT}:${CMA_SERVER_PORT}"
    fi
  fi

  if [ "$AIRGAPPED" = true ]; then
    DOCKER_PORT_ARGS+=" "
    if [ "${PYPI_WEBSERVER_PORT}" -gt "-1" ]; then
      # since inner & outer port may be configured differently, acquire inner port from the image
      INNER_PYPY_WEBSERVER_PORT=$(docker image inspect "${CMA_DOCKER_IMAGE}" | grep -m1 "INNER_PYPY_WEBSERVER_PORT=" | grep -o "[0-9][0-9]*")
      if [ "${PYPI_WEBSERVER_PORT}" == "0" ]; then
        # Random port will be selected
        DOCKER_PORT_ARGS+="-p $INNER_PYPY_WEBSERVER_PORT"
      else
        # Normal port publishing
        DOCKER_PORT_ARGS+="-p ${PYPI_WEBSERVER_PORT}:${INNER_PYPY_WEBSERVER_PORT}"
      fi
    fi
  fi

  DOCKER_ENV_ARGS="-e CMA_SERVER_PORT=${CMA_SERVER_PORT} -e CMA_DOCKER_CONTAINER_NAME=${DOCKER_CONTAINER_NAME} -e CMA_VAULT=${CMA_VAULT} -e CMA_SERVER_EXTRA_PROFILE=${CMA_SERVER_EXTRA_PROFILE} "
  if [ -z "${NEW_CMA_IMAGE_CREATED}" ]; then
    DOCKER_ENV_ARGS+="-e CMA_SERVER_PROFILE=$(get_docker_server_profiles) "
  fi

  DOCKER_MOUNT_POINTS="-v ${CMA_HOST_LOGS_DIR}:${CMA_CONTAINER_LOGS_DIR} -v ${CMA_HOST_DATA_DIR}:${CMA_CONTAINER_DATA_DIR} -v ${CMA_HOST_CONF_DIR}:${CMA_CONTAINER_CONF_DIR} -v ${CMA_HOST_SSL_DIR}:${CMA_CONTAINER_SSL_DIR}"

  CMA_HOME="/$(basename $AM2CM_ROOT_DIR)"
  # NOTE: do NOT quote DOCKER_PORT_ARGS here, since then bash will pass it as a single string to the docker command, instead of expanding it with the spaces
  # i.e. with quotes, it DOES NOT work
  docker run --cap-add=IPC_LOCK -d --name "${DOCKER_CONTAINER_NAME}" ${DOCKER_PORT_ARGS} ${DOCKER_MOUNT_POINTS} ${DOCKER_ENV_ARGS} ${DOCKER_EXTRA_ARGS} "${CMA_DOCKER_IMAGE}" 2>&1 >> "$LOG_FILE"

  LAST_DOCKER_CONTAINER_ID=$(get_container_id)
  if [ -z "${LAST_DOCKER_CONTAINER_ID}" ]; then
    echo "Failed."
  else
    echo "Done."
  fi
}

rebuild_container() {
  stop_container
  delete_container
  run_container
}

start_container() {
  docker start "${DOCKER_CONTAINER_NAME}" >>"$LOG_FILE" 2>&1
  LAST_DOCKER_CONTAINER_ID=$(get_container_id)
}

print_container_info() {
  if docker ps -q -f name="${DOCKER_CONTAINER_NAME}" >/dev/null 2>&1; then
    echo "Container-name: ${DOCKER_CONTAINER_NAME}" | tee -a "$LOG_FILE"
    echo "Container-id: ${LAST_DOCKER_CONTAINER_ID}" | tee -a "$LOG_FILE"
    if [ "${DOCKER_HOST_PORT}" == "-1" ]; then
    # Start without port publishing at all
      echo "The cma-server will be available in about 30 seconds. No port publishing has been set up." | tee -a "$LOG_FILE"
    elif [ "${DOCKER_HOST_PORT}" == "0" ]; then
    # Publish to random port
      echo "The cma-server will be available in about 30 seconds on a random port. Check 'docker ps' output for info." | tee -a "$LOG_FILE"
    else
    # Normal port publishing
      CMA_SERVER_PROTOCOL=$(get_cma_server_protocol)
      echo "The cma-server will be available at ${CMA_SERVER_PROTOCOL}://localhost:${DOCKER_HOST_PORT} in about 30 seconds " | tee -a "$LOG_FILE"
    fi
  else
    echo "The container failed to start!" | tee -a "$LOG_FILE"
    exit 1
  fi
}

# storing the required variables in an .env file
write_env_file() {
  env_file=${1:-.env}
  cat >"$env_file" <<EOF
export LAST_CMA_DOCKER_IMAGE="\${LAST_CMA_DOCKER_IMAGE:-${LAST_CMA_DOCKER_IMAGE}}"
export LAST_DOCKER_CONTAINER_ID="\${LAST_DOCKER_CONTAINER_ID:-${LAST_DOCKER_CONTAINER_ID}}"
export CMA_DOCKER_IMAGE_VERSION="\${CMA_DOCKER_IMAGE_VERSION:-${CMA_DOCKER_IMAGE_VERSION}}"
export SKIP_PORT_PUBLISHING="\${SKIP_PORT_PUBLISHING:-${SKIP_PORT_PUBLISHING}}"
export CUSTOM_DOCKER_IP_SETTINGS="\${CUSTOM_DOCKER_IP_SETTINGS:-${CUSTOM_DOCKER_IP_SETTINGS}}"
EOF
if [ -n "${CMA_NETWORK_NAME}" ]; then
  echo "export CMA_NETWORK_NAME=\"${CMA_NETWORK_NAME}\"" >> "$env_file"
  echo "export CMA_NETWORK_EXTERNAL=\"${CMA_NETWORK_EXTERNAL}\"" >> "$env_file"
fi
}

update_server_profiles() {
  if [ -f "${CMA_SERVER_CONF_FILE}" ]; then
    # update profiles
    local old_server_profiles=$(get_cma_conf_value "cma.server.profiles")
    local new_server_profiles=$(get_server_profiles)
    if [ "${old_server_profiles}" != ${new_server_profiles} ]; then
      set_cma_conf_value "cma.server.profiles" "${new_server_profiles}"
      echo "Changed server profile from ${old_server_profiles} to ${new_server_profiles} " | tee -a "$LOG_FILE"
    fi
  fi
}

stop_standalone() {
  stop_container
}

stop_distributed() {
  echo "Stopping containers.."

  export CMA_SERVER_DOCKER_IMAGE="${CMA_DOCKER_IMAGE_BASE}"
  export DOCKER_IMAGE_VERSION="${CMA_DOCKER_IMAGE_VERSION}"
  export CMA_HOME_DIR="${ROOT_DIR}"
  export CMA_ROOT_DIR="/$(basename ${ROOT_DIR})"
  if [ "${SKIP_PORT_PUBLISHING}" == "false" ]; then
    export EXTRA_PORT_SETTINGS_YML="${ROOT_DIR}/docker/cma.distributed.port.settings.yml"
    export CMA_DB_EXTRA_PORT_SETTINGS_YML="${ROOT_DIR}/docker/cma-master-db.port.settings.yml"
    export CMA_VAULT_EXTRA_PORT_SETTINGS_YML="${ROOT_DIR}/docker/cma-vault.port.settings.yml"
  fi

  if [ "${CUSTOM_DOCKER_IP_SETTINGS}" == "true" ]; then
    export EXTRA_IP_SETTINGS_YML="${ROOT_DIR}/docker/cma.distributed.custom.ip.settings.yml"
    export CMA_DB_EXTRA_IP_SETTINGS_YML="${ROOT_DIR}/docker/cma-master-db.custom.ip.settings.yml"
    export CMA_VAULT_EXTRA_IP_SETTINGS_YML="${ROOT_DIR}/docker/cma-vault.custom.ip.settings.yml"
  fi

  cmd=(docker compose -p "${CMA_INSTANCE_NAME}" -f "${ROOT_DIR}/docker/cma.distributed.yml")
  [[ -n "$EXTRA_PORT_SETTINGS_YML" ]] && cmd+=(-f "$EXTRA_PORT_SETTINGS_YML")
  [[ -n "$EXTRA_IP_SETTINGS_YML" ]] && cmd+=(-f "$EXTRA_IP_SETTINGS_YML")
  if [[ $ACTIVE_PROFILES =~ "postgres" ]]; then
    cmd+=(-f "${ROOT_DIR}/docker/cma-master-db.yml")
    [[ -n "${CMA_DB_EXTRA_PORT_SETTINGS_YML}" ]] && cmd+=(-f "${CMA_DB_EXTRA_PORT_SETTINGS_YML}")
    [[ -n "${CMA_DB_EXTRA_IP_SETTINGS_YML}" ]] && cmd+=(-f "${CMA_DB_EXTRA_IP_SETTINGS_YML}")
  elif [[ $ACTIVE_PROFILES =~ "mysql" ]]; then
    cmd+=(-f "${ROOT_DIR}/docker/cma-master-mysql-db.yml")
    [[ -n "${CMA_DB_EXTRA_PORT_SETTINGS_YML}" ]] && cmd+=(-f "${CMA_DB_EXTRA_PORT_SETTINGS_YML}")
    [[ -n "${CMA_DB_EXTRA_IP_SETTINGS_YML}" ]] && cmd+=(-f "${CMA_DB_EXTRA_IP_SETTINGS_YML}")
  fi

  if [ "$CMA_VAULT" = true ]; then
    cmd+=(-f "${ROOT_DIR}/docker/cma-vault.yml")
    [[ -n "${CMA_VAULT_EXTRA_PORT_SETTINGS_YML}" ]] && cmd+=(-f "${CMA_VAULT_EXTRA_PORT_SETTINGS_YML}")
    [[ -n "${CMA_VAULT_EXTRA_IP_SETTINGS_YML}" ]] && cmd+=(-f "${CMA_VAULT_EXTRA_IP_SETTINGS_YML}")
  fi

  cmd+=(down)
  "${cmd[@]}"

  echo "Removing volumes.."
  docker volume rm $(docker volume ls | grep "${CMA_INSTANCE_NAME}_" | tr -s ' ' | cut -d ' ' -f 2)
}

stop_master() {
  stop_distributed
}

stop_server() {
  stop_distributed
}

stop_agent() {
  echo "Stopping agent.."

  export CMA_AGENT_DOCKER_IMAGE
  export DOCKER_IMAGE_VERSION="${CMA_DOCKER_IMAGE_VERSION}"
  export CMA_HOME_DIR="${ROOT_DIR}"
  export CMA_ROOT_DIR="/$(basename ${ROOT_DIR})"

  if [ "${SKIP_PORT_PUBLISHING}" == "false" ]; then
    export EXTRA_PORT_SETTINGS_YML="${ROOT_DIR}/docker/cma.agent.port.settings.yml"
    export CMA_DB_EXTRA_PORT_SETTINGS_YML="${ROOT_DIR}/docker/cma-agent-db.port.settings.yml"
  fi

  if [ "${CUSTOM_DOCKER_IP_SETTINGS}" == "true" ]; then
    export EXTRA_IP_SETTINGS_YML="${ROOT_DIR}/docker/cma.agent.custom.ip.settings.yml"
    export CMA_DB_EXTRA_IP_SETTINGS_YML="${ROOT_DIR}/docker/cma-agent-db.custom.ip.settings.yml"
  fi

  cmd=(docker compose -p "${CMA_INSTANCE_NAME}" -f "${ROOT_DIR}/docker/cma.agent.yml")
  [[ -n "$EXTRA_PORT_SETTINGS_YML" ]] && cmd+=(-f "$EXTRA_PORT_SETTINGS_YML")
  [[ -n "$EXTRA_IP_SETTINGS_YML" ]] && cmd+=(-f "$EXTRA_IP_SETTINGS_YML")
  if [[ $CMA_SERVER_EXTRA_PROFILE =~ "postgres" ]]; then
    cmd+=(-f "${ROOT_DIR}/docker/cma-agent-db.yml")
    [[ -n "${CMA_DB_EXTRA_PORT_SETTINGS_YML}" ]] && cmd+=(-f "${CMA_DB_EXTRA_PORT_SETTINGS_YML}")
    [[ -n "${CMA_DB_EXTRA_IP_SETTINGS_YML}" ]] && cmd+=(-f "${CMA_DB_EXTRA_IP_SETTINGS_YML}")
  elif [[ $CMA_SERVER_EXTRA_PROFILE =~ "mysql" ]]; then
    cmd+=(-f "${ROOT_DIR}/docker/cma-agent-mysql-db.yml")
    [[ -n "${CMA_DB_EXTRA_PORT_SETTINGS_YML}" ]] && cmd+=(-f "${CMA_DB_EXTRA_PORT_SETTINGS_YML}")
    [[ -n "${CMA_DB_EXTRA_IP_SETTINGS_YML}" ]] && cmd+=(-f "${CMA_DB_EXTRA_IP_SETTINGS_YML}")
  fi
  cmd+=(down)
  "${cmd[@]}"

  echo "Removing volumes.."
  docker volume rm $(docker volume ls | grep "${CMA_INSTANCE_NAME}_" | tr -s ' ' | cut -d ' ' -f 2)
}

start_standalone() {

  # check if image exists otherwise generate one
  if ! docker image inspect "${CMA_DOCKER_IMAGE}" >/dev/null 2>&1; then
    build_image "${CMA_STANDALONE_DIST_TYPE}"
  else
    if [ "${LAST_CMA_DOCKER_IMAGE}" != "${CMA_DOCKER_IMAGE}" ]; then
      while true; do
        if [ -z "${LAST_CMA_DOCKER_IMAGE}" ]; then
          BUILD_CMA_IMAGE=r
        else
          read -r -p "There is a ${CMA_DOCKER_IMAGE_BASE} image with the ${CMA_DOCKER_IMAGE_VERSION} tag. Do you want to [c]ontinue or [r]ebuild? [c]:" BUILD_CMA_IMAGE
        fi
        BUILD_CMA_IMAGE=${BUILD_CMA_IMAGE:-c}
        case $BUILD_CMA_IMAGE in
        [rR])
          rebuild_image
          break
          ;;
        [cC])
          echo "Continue with the existing image..."
          break
          ;;
        *) echo invalid response ;;
        esac
      done
    fi
  fi

  # check if $DOCKER_CONTAINER_NAME exists. Ask the user whether to continue or recreate it.
  if [ ! "$(docker ps -q -f name="${DOCKER_CONTAINER_NAME}")" ]; then
    if docker container inspect "${DOCKER_CONTAINER_NAME}" >/dev/null 2>&1; then
      while true; do
        if [ "${LAST_DOCKER_CONTAINER_ID}" = $(get_container_id) ]; then
          read -r -p "The container ${DOCKER_CONTAINER_NAME} exists. Do you want to [c]ontinue or [r]ebuild? [c]:" BUILD_CMA_CONTAINER
        fi
        BUILD_CMA_CONTAINER=${BUILD_CMA_CONTAINER:-c}
        case $BUILD_CMA_CONTAINER in
        [rR])
          echo "Rebuilding the ${DOCKER_CONTAINER_NAME} container..."
          rebuild_container
          break
          ;;
        [cC])
          echo "Continue with the existing container..."
          start_container
          break
          ;;
        *) echo invalid response ;;
        esac
      done
    else
      run_container
    fi
  fi

  print_container_info
}


start_distributed() {
  echo "Start distributed CMA with docker compose"

  # check if image exists otherwise generate one
  if ! docker image inspect "${CMA_DOCKER_IMAGE}" >/dev/null 2>&1; then
    build_image "${CMA_DISTRIBUTED_DIST_TYPE}"
  fi

  # check specified host port
  if [ "$CMA_SKIP_HOST_PORT_CHECK" = true ]; then
    echo "Host port verification skipped!" | tee -a "$LOG_FILE"
  elif [ "${DOCKER_HOST_PORT}" -gt "0" ]; then
    if ! verify_port "${DOCKER_HOST_PORT}"; then
      echo "Host port $DOCKER_HOST_PORT is being used by another process!" | tee -a "$LOG_FILE"
      exit 1
    fi
    echo "Host port verification done!" | tee -a "$LOG_FILE"
  fi

  # configure ssl ports
  if is_ssl_active; then
    export CMA_AUTH_SERVER_HOST_PORT=${CMA_AUTH_SERVER_HOST_PORT:-9443}
    export CMA_GATEWAY_SERVER_HOST_PORT=${DOCKER_HOST_PORT:-8093}
    export CMA_SERVER_HOST_PORT=${CMA_SERVER_HOST_PORT:-8893}
    export CMA_SERVER_PROTOCOL=https
    export CMA_GATEWAY_SERVER_PROTOCOL=https
    export CMA_SERVER_SSL=true
  fi

  # gRPC TLS is always enabled in Docker; ensure a keystore exists.
  # If CMA_KEYSTORE_FILE is not set, read it from ssl-server.properties (written by setup-ssl.sh).
  # If ssl-server.properties is also absent, auto-generate a self-signed cert via setup-ssl.sh now.
  local ssl_server_conf="${CMA_HOST_CONF_DIR}/ssl-server.properties"
  if [ -z "${CMA_KEYSTORE_FILE}" ] || [ ! -f "${CMA_KEYSTORE_FILE}" ]; then
    if [ -f "${ssl_server_conf}" ]; then
      export CMA_KEYSTORE_FILE=$(get_cma_conf_value "cma.server.ssl.key-store" "${ssl_server_conf}")
      export CMA_KEYSTORE_PASSWORD=$(get_cma_conf_value "cma.server.ssl.key-store-password" "${ssl_server_conf}")
      export CMA_KEYSTORE_TYPE=$(get_cma_conf_value "cma.server.ssl.key-store-type" "${ssl_server_conf}")
    else
      echo "No keystore configured — generating self-signed gRPC keystore via setup-ssl.sh..."
      local san_args=()
      if [ -n "${CMA_GRPC_EXTRA_SANS}" ]; then
        san_args=("-san" "${CMA_GRPC_EXTRA_SANS}")
      fi
      "${ROOT_DIR}/bin/setup-ssl.sh" generate -s master -d docker \
        --app-data-dir "${CMA_APP_DATA_DIR:-${HOME}/.cma}" \
        --instance-name "${CMA_INSTANCE_NAME:-cma-server}" \
        --generate-only "${san_args[@]+"${san_args[@]}"}"
      echo "setup-ssl.sh completed. Loading generated keystore info from ssl_server_conf=${ssl_server_conf}"
      if [ -f "${ssl_server_conf}" ]; then
        export CMA_KEYSTORE_FILE=$(get_cma_conf_value "cma.server.ssl.key-store" "${ssl_server_conf}")
        export CMA_KEYSTORE_PASSWORD=$(get_cma_conf_value "cma.server.ssl.key-store-password" "${ssl_server_conf}")
        export CMA_KEYSTORE_TYPE=$(get_cma_conf_value "cma.server.ssl.key-store-type" "${ssl_server_conf}")
      fi
    fi
    echo "gRPC TLS: keystore=${CMA_KEYSTORE_FILE}, type=${CMA_KEYSTORE_TYPE}"
  fi

  # configure custom docker network
  if [ -n "${CUSTOM_DOCKER_NETWORK}" ]; then
    export CMA_NETWORK_NAME=${CUSTOM_DOCKER_NETWORK}
    echo "Following Docker network will be used: ${CMA_NETWORK_NAME}"
    if [ -n "$(docker network ls --filter name=${CMA_NETWORK_NAME} -q)" ]; then
      export CMA_NETWORK_EXTERNAL=true
    else
      export CMA_NETWORK_EXTERNAL=false
    fi
  fi

  export CMA_SERVER_DOCKER_IMAGE="${CMA_DOCKER_IMAGE_BASE}"
  export DOCKER_IMAGE_VERSION="${CMA_DOCKER_IMAGE_VERSION}"
  export CMA_HOME_DIR="${ROOT_DIR}"
  export CMA_ROOT_DIR="/$(basename ${ROOT_DIR})"
  export SRC_CMA_TOOLS="cma-tools"

  export CMA_GATEWAY_SERVER_HOST_PORT=${CMA_GATEWAY_SERVER_HOST_PORT:-${DOCKER_HOST_PORT}}

  export CMA_SERVER_PROFILES=$(get_server_profiles)
  export CMA_GATEWAY_SERVER_PROFILES=${CMA_SERVER_PROFILES}
  export CMA_AUTH_SERVER_PROFILES=${CMA_SERVER_PROFILES}
  export CMA_SERVER_EXTRA_PROFILE
  # --- LOAD ---
  export CMA_AGENT_AUTH_CONF_FILE="${CMA_HOST_CONF_DIR}/agent-auth.properties"
  load_master_db_conf
  load_auth_db_conf
  load_master_auth_conf
  load_cma_agent_auth_conf

  # --- LOGIC ---
  prompt_for_master_secrets
  export CMA_CLIENT_SECRET
  # Compute auth server URI for agent token acquisition (http/https based on CMA_SERVER_SSL)
  local _auth_proto="http"
  local _auth_port="${CMA_AUTH_SERVER_HOST_PORT:-9000}"
  if [ "${CMA_SERVER_SSL}" = "true" ]; then
    _auth_proto="https"
    _auth_port="${CMA_AUTH_SERVER_HOST_PORT:-9443}"
  fi
  export CMA_AUTH_SERVER_URI="${CMA_AUTH_SERVER_URI:-${_auth_proto}://${CMA_AUTH_SERVER_HOSTNAME:-cma-auth-server.cma.local}:${_auth_port}}"

  # Set DB host for containerized DB before configure_db_type exports DB vars
  if [[ $ACTIVE_PROFILES =~ "postgres" ]] || [[ $ACTIVE_PROFILES =~ "mysql" ]]; then
    export CMA_DB_HOST="${CMA_INSTANCE_NAME:-cma-master}.db.cma.local"
    export CMA_HOST_DB_DIR=${CMA_HOST_DB_DIR:-${CMA_HOST_INSTANCE_DIR}/db}
    mkdir -p "${CMA_HOST_DB_DIR}"
    # Generate db.properties and auth-db.properties on the host so all containers
    # can read them via the shared app-conf volume mount.
    if [[ $ACTIVE_PROFILES =~ "postgres" ]]; then
        configure_postgres;
    fi

    if [[ $ACTIVE_PROFILES =~ "mysql" ]]; then
        configure_mysql;
    fi

  elif [ -n "${CMA_DBA_PASS}" ]; then
    # H2 with an explicit password: write credentials to db.properties so
    # Spring Boot can resolve cma.db.pass without relying on an insecure default.
    export CMA_DBA_USER="${CMA_DBA_USER:-cma}"
    export CMA_AUTH_DBA_USER="${CMA_AUTH_DBA_USER:-${CMA_DBA_USER}}"
    export CMA_AUTH_DBA_PASS="${CMA_AUTH_DBA_PASS:-${CMA_DBA_PASS}}"
  fi

    create_master_db_conf
    create_auth_db_conf


  configure_db_type
  ensure_internal_keystore_password "${CMA_HOST_CONF_DIR}"
  DATA_DIR="${CMA_HOST_DATA_DIR}" ensure_grpc_keystore "${CMA_HOST_CONF_DIR}"

  # --- CREATE ---
  create_master_auth_conf
  create_cma_agent_auth_conf
  create_master_db_conf
  create_auth_db_conf

  export CMA_CONTAINER_DATA_DIR="${CMA_CONTAINER_DATA_DIR}"

  export CMA_HOST_CONF_DIR
  export CMA_HOST_DATA_DIR
  export CMA_HOST_LOGS_DIR
  export CMA_HOST_SSL_DIR
  export CMA_CONTAINER_INSTANCE_DIR
  export CMA_VAULT

  export CMA_AUTH_SERVER_LOG_FILE="${CMA_CONTAINER_LOGS_DIR}"/cma-auth-server.log
  export CMA_GATEWAY_SERVER_LOG_FILE="${CMA_CONTAINER_LOGS_DIR}"/cma-gateway-server.log

  if [ -f "${CMA_VAULT_SERVER_CONF_FILE}" ]; then
    export VAULT_SERVER_HOST=$(get_cma_conf_value "vault.server.host" "${CMA_VAULT_SERVER_CONF_FILE}")
    export VAULT_SERVER_PORT=$(get_cma_conf_value "vault.server.port" "${CMA_VAULT_SERVER_CONF_FILE}")
    export VAULT_SERVER_SCHEME=$(get_cma_conf_value "vault.server.scheme" "${CMA_VAULT_SERVER_CONF_FILE}")
  fi

  if [ "${SKIP_PORT_PUBLISHING}" == "false" ]; then
    export EXTRA_PORT_SETTINGS_YML="${ROOT_DIR}/docker/cma.distributed.port.settings.yml"
    export CMA_DB_EXTRA_PORT_SETTINGS_YML="${ROOT_DIR}/docker/cma-master-db.port.settings.yml"
    export CMA_VAULT_EXTRA_PORT_SETTINGS_YML="${ROOT_DIR}/docker/cma-vault.port.settings.yml"
  fi

  if [ "${CUSTOM_DOCKER_IP_SETTINGS}" == "true" ]; then
    export EXTRA_IP_SETTINGS_YML="${ROOT_DIR}/docker/cma.distributed.custom.ip.settings.yml"
    export CMA_DB_EXTRA_IP_SETTINGS_YML="${ROOT_DIR}/docker/cma-master-db.custom.ip.settings.yml"
    export CMA_VAULT_EXTRA_IP_SETTINGS_YML="${ROOT_DIR}/docker/cma-vault.custom.ip.settings.yml"
  fi

  cmd=(docker compose -p "${CMA_INSTANCE_NAME}" -f "${ROOT_DIR}/docker/cma.distributed.yml")
  [[ -n "$EXTRA_PORT_SETTINGS_YML" ]] && cmd+=(-f "$EXTRA_PORT_SETTINGS_YML")
  [[ -n "$EXTRA_IP_SETTINGS_YML" ]] && cmd+=(-f "$EXTRA_IP_SETTINGS_YML")
  if [[ $ACTIVE_PROFILES =~ "postgres" ]]; then
    cmd+=(-f "${ROOT_DIR}/docker/cma-master-db.yml")
    [[ -n "${CMA_DB_EXTRA_PORT_SETTINGS_YML}" ]] && cmd+=(-f "${CMA_DB_EXTRA_PORT_SETTINGS_YML}")
    [[ -n "${CMA_DB_EXTRA_IP_SETTINGS_YML}" ]] && cmd+=(-f "${CMA_DB_EXTRA_IP_SETTINGS_YML}")
  elif [[ $ACTIVE_PROFILES =~ "mysql" ]]; then
    cmd+=(-f "${ROOT_DIR}/docker/cma-master-mysql-db.yml")
    [[ -n "${CMA_DB_EXTRA_PORT_SETTINGS_YML}" ]] && cmd+=(-f "${CMA_DB_EXTRA_PORT_SETTINGS_YML}")
    [[ -n "${CMA_DB_EXTRA_IP_SETTINGS_YML}" ]] && cmd+=(-f "${CMA_DB_EXTRA_IP_SETTINGS_YML}")
  fi

  if [ "$CMA_VAULT" = true ]; then
    cmd+=(-f "${ROOT_DIR}/docker/cma-vault.yml")
    [[ -n "${CMA_VAULT_EXTRA_PORT_SETTINGS_YML}" ]] && cmd+=(-f "${CMA_VAULT_EXTRA_PORT_SETTINGS_YML}")
    [[ -n "${CMA_VAULT_EXTRA_IP_SETTINGS_YML}" ]] && cmd+=(-f "${CMA_VAULT_EXTRA_IP_SETTINGS_YML}")
  fi

  cmd+=(up -d)
  "${cmd[@]}"

  # get cma-server container id
  LAST_DOCKER_CONTAINER_ID=$(docker --log-level error compose -p "${CMA_INSTANCE_NAME}" -f "${ROOT_DIR}"/docker/cma.distributed.yml ps -q cma-server)

  # Wait for cma-server to reach 'running' state. With depends_on: service_healthy, Docker will not
  # start cma-server until cma-auth-server's health check passes (after its start-period), so
  # docker compose up -d returns before cma-server has had a chance to start.
  echo "Waiting up to ${CMA_DOCKER_STARTUP_TIMEOUT}s for cma-server to start..."
  local timeout_end=$((SECONDS + CMA_DOCKER_STARTUP_TIMEOUT))
  while [ $SECONDS -lt $timeout_end ]; do
    local status
    status=$(docker inspect -f '{{.State.Status}}' "${LAST_DOCKER_CONTAINER_ID}" 2>/dev/null || echo "")
    if [ "${status}" = "running" ]; then
      echo "cma-server is running."
      break
    fi
    sleep 2
  done
}

start_master() {
  start_distributed
}

start_server() {
  start_distributed
}

start_agent() {
  echo "Start CMA agent with docker compose"

  export CMA_AGENT_AUTH_CONF_FILE="${CMA_HOST_CONF_DIR}/agent-auth.properties"
  load_cma_agent_auth_conf
  load_agent_db_conf

  # check if image exists otherwise generate one
  if ! docker image inspect "${CMA_AGENT_DOCKER_IMAGE}" >/dev/null 2>&1; then
    build_agent_image
  fi

  # check specified host port
  if [ "$CMA_SKIP_HOST_PORT_CHECK" = true ]; then
    echo "Host port verification skipped!" | tee -a "$LOG_FILE"
  elif [ "${CMA_AGENT_DOCKER_HOST_PORT}" -gt "0" ]; then
    if ! verify_port "${CMA_AGENT_DOCKER_HOST_PORT}"; then
      echo "Host port CMA_AGENT_DOCKER_HOST_PORT is being used by another process!" | tee -a "$LOG_FILE"
      exit 1
    fi
    echo "Host port verification done!" | tee -a "$LOG_FILE"
  fi

  if [ "${SKIP_PORT_PUBLISHING}" == "false" ]; then
    export EXTRA_PORT_SETTINGS_YML="${ROOT_DIR}/docker/cma.agent.port.settings.yml"
    export CMA_DB_EXTRA_PORT_SETTINGS_YML="${ROOT_DIR}/docker/cma-agent-db.port.settings.yml"
  fi

  if [ "${CUSTOM_DOCKER_IP_SETTINGS}" == "true" ]; then
    export EXTRA_IP_SETTINGS_YML="${ROOT_DIR}/docker/cma.agent.custom.ip.settings.yml"
    export CMA_DB_EXTRA_IP_SETTINGS_YML="${ROOT_DIR}/docker/cma-agent-db.custom.ip.settings.yml"
  fi

  # Guard: refuse to start an agent in an instance dir that is already used by a
  # CMA Master.  cma-master.properties is written only by cma-master.sh (Docker mode)
  # and is never created by agent code, making it a reliable master-role indicator.
  if [ -f "${CMA_HOST_INSTANCE_DIR}/conf/cma-master.properties" ]; then
    echo "Error: instance directory '${CMA_HOST_INSTANCE_DIR}' is already used by a CMA Master."
    echo "Use a different --instance-name (current: ${CMA_INSTANCE_NAME}) or --app-data-dir for the agent."
    exit 1
  fi

  export CMA_AGENT_HOST_PORT=${CMA_AGENT_DOCKER_HOST_PORT:-9090}
  # configure ssl ports
  if is_ssl_active; then
    export CMA_AGENT_HOST_PORT=${CMA_AGENT_HOST_PORT:-9093}
    export CMA_AGENT_PROTOCOL=https
    export CMA_AGENT_SSL
  fi

  # Resolve CMA_GATEWAY_URL:
  #   co-located mode — agent and master run in the same Docker stack/network;
  #                     derive the gateway URL from the well-known container hostname.
  #   external mode   — CMA_GATEWAY_URL is passed in via --gateway-url; use it as-is.
  if [ -z "${CMA_GATEWAY_URL}" ]; then
    # Use the master's HTTP API scheme: HTTPS when SSL is active, HTTP otherwise.
    # The gateway container always listens on port 8090 regardless of HTTP/HTTPS;
    # only the host-side port mapping differs.
    local _gateway_scheme=$( is_ssl_active && echo "https" || echo "http" )
    CMA_GATEWAY_URL="${_gateway_scheme}://${CMA_GATEWAY_SERVER_HOSTNAME:-cma-gateway-server.cma.local}:${CMA_GATEWAY_SERVER_HOST_PORT:-8090}"
    echo "Co-located mode: derived CMA_GATEWAY_URL=${CMA_GATEWAY_URL}" | tee -a "$LOG_FILE"
  fi
  export CMA_GATEWAY_URL

  prompt_for_agent_secrets
  create_cma_agent_auth_conf

  # configure custom docker network
  if [ -n "${CUSTOM_DOCKER_NETWORK}" ]; then
    export CMA_NETWORK_NAME=${CUSTOM_DOCKER_NETWORK}
    echo "Following Docker network will be used: ${CMA_NETWORK_NAME}"
  fi

  # Resolve the effective network name (may have been set by CUSTOM_DOCKER_NETWORK above or by
  # the caller; otherwise falls back to the same default as the compose file itself).
  local effective_network="${CMA_NETWORK_NAME:-${CMA_STACK_NAME:-cma}-network}"
  export CMA_NETWORK_NAME="${effective_network}"

  # If the network already exists (e.g., created by the master stack running as a separate compose
  # project), mark it external so this project joins rather than tries to own it.
  # Without external:true, Docker Compose would either error ("network belongs to another project")
  # or create an isolated network, making the agent unable to reach the master.
  if docker network ls --format '{{.Name}}' | grep -qx "${effective_network}"; then
    export CMA_NETWORK_EXTERNAL=true
    echo "Docker network '${effective_network}' already exists — joining as external network."
  else
    export CMA_NETWORK_EXTERNAL=false
  fi

  export CMA_CONTAINER_INSTANCE_DIR="/home/cma/cma-agent"
  export DOCKER_IMAGE_VERSION="${CMA_DOCKER_IMAGE_VERSION}"
  export CMA_HOME_DIR="${ROOT_DIR}"
  export CMA_ROOT_DIR="/$(basename ${ROOT_DIR})"

  export CMA_SERVER_PROFILES=$(get_server_profiles)
  export CMA_SERVER_EXTRA_PROFILE
  export CMA_CONTAINER_DATA_DIR="${CMA_CONTAINER_DATA_DIR}"


  export CMA_HOST_CONF_DIR
  export CMA_HOST_DATA_DIR
  export CMA_HOST_LOGS_DIR
  export CMA_HOST_SSL_DIR
  export CMA_AGENT_DOCKER_IMAGE
  export CMA_AGENT_HOSTNAME="${CMA_INSTANCE_NAME:-cma-agent}.cma.local"

  if [[ $CMA_SERVER_EXTRA_PROFILE =~ "postgres" ]] || [[ $CMA_SERVER_EXTRA_PROFILE =~ "mysql" ]]; then
    export CMA_AGENT_DB_HOST="${CMA_INSTANCE_NAME:-cma-agent}.db.cma.local"
    export CMA_HOST_DB_DIR=${CMA_HOST_DB_DIR:-${CMA_HOST_INSTANCE_DIR}/db}
    mkdir -p "${CMA_HOST_DB_DIR}"
  fi

  create_agent_db_conf
  ensure_internal_keystore_password "${CMA_HOST_CONF_DIR}"

  export CMA_VAULT

  cmd=(docker compose -p "${CMA_INSTANCE_NAME}" -f "${ROOT_DIR}/docker/cma.agent.yml")
  [[ -n "$EXTRA_PORT_SETTINGS_YML" ]] && cmd+=(-f "$EXTRA_PORT_SETTINGS_YML")
  [[ -n "$EXTRA_IP_SETTINGS_YML" ]] && cmd+=(-f "$EXTRA_IP_SETTINGS_YML")
  if [[ $CMA_SERVER_EXTRA_PROFILE =~ "postgres" ]]; then
    cmd+=(-f "${ROOT_DIR}/docker/cma-agent-db.yml")
    [[ -n "${CMA_DB_EXTRA_PORT_SETTINGS_YML}" ]] && cmd+=(-f "${CMA_DB_EXTRA_PORT_SETTINGS_YML}")
    [[ -n "${CMA_DB_EXTRA_IP_SETTINGS_YML}" ]] && cmd+=(-f "${CMA_DB_EXTRA_IP_SETTINGS_YML}")
  elif [[ $CMA_SERVER_EXTRA_PROFILE =~ "mysql" ]]; then
    cmd+=(-f "${ROOT_DIR}/docker/cma-agent-mysql-db.yml")
    [[ -n "${CMA_DB_EXTRA_PORT_SETTINGS_YML}" ]] && cmd+=(-f "${CMA_DB_EXTRA_PORT_SETTINGS_YML}")
    [[ -n "${CMA_DB_EXTRA_IP_SETTINGS_YML}" ]] && cmd+=(-f "${CMA_DB_EXTRA_IP_SETTINGS_YML}")
  fi
  cmd+=(up -d)
  "${cmd[@]}"

  # get cma-agent container id
  LAST_DOCKER_CONTAINER_ID=$(docker --log-level error compose -p "${CMA_INSTANCE_NAME}" -f "${ROOT_DIR}"/docker/cma.agent.yml ps -q cma-agent)
}
