#!/bin/bash DEBUG_SERVER="debug-server" IMG_URL="gcr.io/cloud-tagging-10302018/gtm-cloud-image:stable" TMP_DIR="$(mktemp -d)" ZIP_URL="https://storage.googleapis.com/serverside-tagging/gtm-cloud-image.zip" TEST_ENV="testing" PROD_ENV="production" PROD1_ENV="production1" EXISTING_APP_ENGINE_SERVICE_PROMPT=\ "There is an existing App Engine application that does not look like a Server-side GTM server. Continuing with this script will override the existing deployment. Do you wish to continue? (y/N): " WISH_TO_CONTINUE="Do you wish to continue? (y/N): " WELCOME_TEXT=\ "Please input the following information to set up your tagging server. For more information about the configuration, input '?'. To use the recommended setting or your current setting, leave blank. Press Enter to continue." CONTAINER_CONFIG_HELP=\ " The container config describes your server-side container. Copy it from Google Tag Manager." POLICY_SCRIPT_HELP=\ " The policy script URL is an optional URL that specifies the policies that govern custom template permissions. A value of '' means that there is no policy script URL. Enter 'None' to clear the URL. For more information, see: https://developers.google.com/tag-manager/templates/policies" DEPLOYMENT_TYPE_HELP=\ " A $TEST_ENV deployment type deploys the server-side container to the App Engine Standard Environment with a single server. It is suitable for limited traffic volume and should be free for low amounts of traffic; however, you may incur some costs depending on traffic volume. A $PROD_ENV deployment type deploys the server-side container to the App Engine Flexible Environment and allocates additional servers to ensure redundancy and avoid data loss in case of outages or capacity limitations. The only supported options are: ${TEST_ENV} or ${PROD_ENV}" AUTOSCALING_HELP=\ " Autoscaling will automatically create and shut down servers as the volume of traffic fluctuates over time. The only supported options are: on or off." NUM_INSTANCES_HELP=\ " The number of servers running the container at any given time. Each server costs roughly \$40 per month." MIN_INSTANCES_HELP=\ " The minimum number of servers running the container at any given time. Each server costs roughly \$40 per month. This value is initially set high enough to accommodate the current traffic. The script resets it to the specified value before it completes." MAX_INSTANCES_HELP=\ " The maximum number of servers running the container at any given time. These servers are started up when needed, and turned off when they are not needed. If you find there's constantly the maximum number of servers running, consider setting this number higher." CPU_TARGET_HELP=\ " The target CPU utilization ratio to use when scaling. The default value is recommended. Must be between 0 and 1." REQUEST_LOGGING_HELP=\ " By default, App Engine logs information about every request that it receives. If your tagging server handles a lot of requests per month (e.g. greater than one million), those log messages may incur significant logging charges. The only supported options are: on or off." DOWNGRADE_NOTE=\ "NOTE: You are downgrading from a ${PROD_ENV} deployment to a ${TEST_ENV} deployment. This may cause a drop in availability depending on your traffic." UPGRADE_NOTE=\ "NOTE: You are upgrading from a ${TEST_ENV} deployment to a ${PROD_ENV} deployment. The linked billing account will start incurring charges." SAME_SETTINGS=\ "Note that your configured settings are the same as the current deployment. This action is recommended if you only want to update the server version." MIN_INSTANCES_UPDATE_FAILED_NOTE=" Attempt to reset the minimum number of servers failed. Redeploying the tagging server ... " BETWEEN_ZERO_AND_ONE_REGEX="^(0?\.([0-9]+[1-9]+0*|[1-9]+[0-9]*))$" POSITIVE_INT_REGEX="^[1-9]+[0-9]*$" EXCLUDE_REQUEST_LOGS=\ "NOT LOG_ID(\"appengine.googleapis.com/nginx.request\") AND NOT LOG_ID(\"appengine.googleapis.com/request_log\")" EXCLUDE_REQUEST_LOGS_ESCAPED="$(echo "${EXCLUDE_REQUEST_LOGS}" | sed 's/\//\\\//g')" trap cleanup EXIT trap "exit" INT set -e screen_reader="$(gcloud config get accessibility/screen_reader 2> /dev/null)" gcloud config set accessibility/screen_reader False 2> /dev/null cleanup() { gcloud config set accessibility/screen_reader "${screen_reader}" 2> /dev/null rm -rf "${TMP_DIR}" } create_testing_config() { local config_file="${TMP_DIR}/${TEST_ENV}.yaml" local config=\ "service: default runtime: nodejs18 instance_class: F1 automatic_scaling: max_instances: 1 env_variables: CONTAINER_CONFIG: $1 INCLUDE_DEBUG_SERVER: true POLICY_SCRIPT_URL: $2 NODE_OPTIONS: --max-http-header-size=16384 handlers: - url: /.* secure: always script: auto" echo "${config}" > "${config_file}" echo "${config_file}" } create_flex_tagging_server_config() { local config_file="${TMP_DIR}/${PROD_ENV}.yaml" local config=\ "service: default runtime: nodejs env: flex resources: cpu: 1 memory_gb: 0.5 disk_size_gb: 10 readiness_check: path: '/healthz' failure_threshold: 3 check_interval_sec: 10 liveness_check: path: '/healthz' failure_threshold: 10 check_interval_sec: 10 env_variables: CONTAINER_CONFIG: $1 POLICY_SCRIPT_URL: $2 PREVIEW_SERVER_URL: $3 NODE_OPTIONS: --max-http-header-size=16384" echo "${config}" > "${config_file}" echo "${config_file}" } add_autoscaling_to_config() { echo \ "automatic_scaling: min_num_instances: $2 max_num_instances: $3 cpu_utilization: target_utilization: $4" >> "$1" } add_manual_scaling_to_config() { echo \ "manual_scaling: instances: $2" >> "$1" } create_debug_config() { local config_file="${TMP_DIR}/debug.yaml" local config=\ "service: ${DEBUG_SERVER} runtime: nodejs18 instance_class: F1 automatic_scaling: max_instances: 1 env_variables: CONTAINER_CONFIG: $1 RUN_AS_DEBUG_SERVER: true NODE_OPTIONS: --max-http-header-size=16384" echo "${config}" > "${config_file}" echo "${config_file}" } create_dispatch_config() { local config_file="${TMP_DIR}/dispatch.yaml" local config="dispatch:" echo "${config}" > "${config_file}" echo "${config_file}" } wait_all_operations_complete() { local pending_operations pending_operations="$(gcloud app operations list --pending\ --format="table(id,status)" -q 2> /dev/null)" || true operations="$(echo "${pending_operations}" | sed -n 's/\([a-z0-9-]\{1,\}\)[[:blank:]]\{1,\}.*/\1/p')" if [[ ! -z "${operations}" ]]; then echo "There are existing operations. Waiting for them to finish before continuing." for operation in ${operations}; do gcloud app operations wait "${operation}" --verbosity=none -q > /dev/null done fi } testing_deployment() { local source_zip="${TMP_DIR}/source.zip" curl -fsSL --output "${source_zip}" "${ZIP_URL}" && unzip -q "${source_zip}" -d "${TMP_DIR}" && rm "${source_zip}" wait_all_operations_complete if ! gcloud app services list --verbosity=none > /dev/null 2> /dev/null; then gcloud app deploy "$(create_testing_config "$1" "$2")" \ --version "${TEST_ENV}" else gcloud app deploy -q "$(create_testing_config "$1" "$2")" \ --version "${TEST_ENV}" --stop-previous-version fi wait_all_operations_complete gcloud app deploy -q "$(create_dispatch_config)" gcloud app services delete -q "${DEBUG_SERVER}" --verbosity=none || true } production_deployment() { local source_zip="${TMP_DIR}/source.zip" curl -fsSL --output "${source_zip}" "${ZIP_URL}" && unzip -q "${source_zip}" -d "${TMP_DIR}" && rm "${source_zip}" gcloud app deploy -q "$(create_debug_config "${container_config}")" \ --version "${PROD_ENV}" preview_server_url="$( gcloud app services browse "${DEBUG_SERVER}" \ --format="value(url)" --no-launch-browser)" production_tagging_deployment "$1" "$2" "$3" "$4" "$5" "${preview_server_url}" wait_all_operations_complete gcloud app deploy -q "$(create_dispatch_config)" gcloud app versions delete -q "${TEST_ENV}" --verbosity=none || true } production_tagging_deployment() { local config config="$(create_flex_tagging_server_config "$1" "$2" "$6")" if [[ "${autoscaling}" == "on" ]]; then add_autoscaling_to_config "${config}" "$3" "$4" "$5" else add_manual_scaling_to_config "${config}" "$3" fi wait_all_operations_complete local is_stopped is_stopped="$(gcloud app versions list\ --format="table(service,id,traffic_split,version.servingStatus)" | sed -n "/default.*${next_deployment_type}.*STOPPED/p")" || true 2> /dev/null if [[ ! -z "${is_stopped}" ]]; then gcloud app versions start -q "${next_deployment_type}" \ --verbosity=none --service default || true fi if ! gcloud app services list --verbosity=none > /dev/null 2> /dev/null; then gcloud app deploy "${config}" \ --image-url "${IMG_URL}" --version "${next_deployment_type}" else gcloud app deploy -q "${config}" \ --image-url "${IMG_URL}" --version "${next_deployment_type}" \ --stop-previous-version fi } attempt_set_min_instances() { curl -sS -X PATCH \ -H "Authorization: Bearer $(gcloud auth print-access-token)" \ -H "Content-Type: application/json" \ -d "{'env': 'flex', 'automaticScaling': {'minTotalInstances': $2}}" \ "https://appengine.googleapis.com/v1\ /apps/$(gcloud config get-value project)\ /services/default/versions/$1\ /?updateMask=automaticScaling.minTotalInstances" > /dev/null } determine_next_production_type() { if [[ $1 == "${PROD_ENV}" ]]; then echo "${PROD1_ENV}" else echo "${PROD_ENV}" fi } extract_from_yaml() { echo "$1" | sed -n "s/[[:blank:]]*$2: \(.*\)/\1/p" } generate_suggested() { echo "$([[ -z "$1" ]] && echo "$2" || echo "Current: $1")" } prompt_container_config() { while [[ -z "${container_config}" || "${container_config}" == '?' ]]; do suggested="$(generate_suggested "${cur_container_config}" "Required")" printf "Container Config (${suggested}): " read container_config if [[ -z "${container_config}" ]]; then container_config="${cur_container_config}" fi is_valid="$(echo "${container_config}" | sed -n '/^[A-Za-z0-9+/=]*$/p')" if [[ "${container_config}" == '?' ]]; then echo "${CONTAINER_CONFIG_HELP}" elif [[ -z "${container_config}" ]]; then echo " Container config cannot be empty." elif [[ -z "${is_valid}" ]]; then echo " Container config is incorrect." container_config="" fi done } prompt_policy_script_url() { while true; do suggested="$(generate_suggested "${cur_policy_script_url}" "Optional")" printf "Policy Script URL (${suggested}): " read policy_script_url if [[ "${policy_script_url}" =~ ^[Nn][Oo][Nn][Ee]$ ]]; then policy_script_url="''" elif [[ "${policy_script_url}" == '""' ]]; then policy_script_url="''" fi if [[ "$policy_script_url" == '?' ]]; then echo "${POLICY_SCRIPT_HELP}" elif [[ -z "${policy_script_url}" ]]; then if [[ ! -z "${cur_policy_script_url}" ]]; then policy_script_url="${cur_policy_script_url}" else policy_script_url="''" fi break else break fi done } prompt_request_logging() { while [[ ("${request_logging}" != "on" && "${request_logging}" != "off") || "${request_logging}" == '?' ]]; do suggested="$(generate_suggested "${cur_request_logging}")" printf "Request Logging (${suggested}): " read request_logging request_logging="$(echo "${request_logging}" | tr '[:upper:]' '[:lower:]')" if [[ "${request_logging}" == '?' ]]; then echo "${REQUEST_LOGGING_HELP}" elif [[ -z "${request_logging}" ]]; then request_logging="${cur_request_logging}" elif [[ "${request_logging}" != "on" && "${request_logging}" != "off" ]]; then echo " The only supported options are: on or off" fi done } prompt_deployment_type() { local suggested_cur_deployment suggested_cur_deployment="${cur_deployment_type}" if [[ "${suggested_cur_deployment}" == "${PROD1_ENV}" ]]; then suggested_cur_deployment="${PROD_ENV}" fi while [[ ("${deployment_type}" != "${TEST_ENV}" && \ "${deployment_type}" != "${PROD_ENV}") || "${deployment_type}" == '?' ]]; do suggested="$(generate_suggested \ "${suggested_cur_deployment}" "${TEST_ENV}/${PROD_ENV}")" printf "Deployment Type (${suggested}): " read deployment_type deployment_type="$(echo "${deployment_type}" | tr '[:upper:]' '[:lower:]')" if [[ -z "${deployment_type}" ]]; then deployment_type="${suggested_cur_deployment}" fi if [[ "${deployment_type}" == '?' ]]; then echo "${DEPLOYMENT_TYPE_HELP}" elif [[ "${deployment_type}" != "${TEST_ENV}" && \ "${deployment_type}" != "${PROD_ENV}" ]]; then echo " The only supported options are: ${TEST_ENV} or ${PROD_ENV}" fi done } prompt_continue_default_no() { while true; do printf "$1" read confirmation confirmation="$(echo "${confirmation}" | tr '[:upper:]' '[:lower:]')" if [[ -z "${confirmation}" || "${confirmation}" == 'n' ]]; then exit 0 fi if [[ "${confirmation}" == "y" ]]; then break fi done } prompt_autoscaling() { while [[ ("${autoscaling}" != "on" && "${autoscaling}" != "off") || \ "${autoscaling}" == '?' ]]; do recommended="on" suggested="$(\ generate_suggested "${cur_autoscaling}" "Recommended: ${recommended}")" printf "Autoscaling (${suggested}): " read autoscaling autoscaling="$(echo "${autoscaling}" | tr '[:upper:]' '[:lower:]')" if [[ "${autoscaling}" == '?' ]]; then echo "${AUTOSCALING_HELP}" elif [[ -z "${autoscaling}" ]]; then if [[ ! -z "${cur_autoscaling}" ]]; then autoscaling="${cur_autoscaling}" else autoscaling="${recommended}" fi elif [[ "${autoscaling}" != "on" && "${autoscaling}" != "off" ]]; then echo " The only supported options are: on or off" fi done } prompt_num_instances() { while [[ ! "${num_instances}" =~ ${POSITIVE_INT_REGEX} || \ "${num_instances}" == '?' ]]; do recommended="3" suggested="$(\ generate_suggested "${cur_num_instances}" "Recommended: ${recommended}")" printf "Number of Servers (${suggested}): " read num_instances if [[ "${num_instances}" == '?' ]]; then echo "${NUM_INSTANCES_HELP}" elif [[ -z "${num_instances}" ]]; then if [[ ! -z "${cur_num_instances}" ]]; then num_instances="${cur_num_instances}" else num_instances="${recommended}" fi elif [[ ! "${num_instances}" =~ ${POSITIVE_INT_REGEX} ]]; then echo " The input must be a positive integer." fi done } prompt_min_instances() { while [[ ! "${min_instances}" =~ ${POSITIVE_INT_REGEX} || \ "${min_instances}" == '?' || "${min_instances}" -le 0 ]]; do recommended="3" suggested="$(\ generate_suggested "${cur_min_instances}" "Recommended: ${recommended}")" printf "Minimum Number of Servers (${suggested}): " read min_instances if [[ "${min_instances}" == '?' ]]; then echo "${MIN_INSTANCES_HELP}" elif [[ -z "${min_instances}" ]]; then if [[ ! -z "${cur_min_instances}" ]]; then min_instances="${cur_min_instances}" else min_instances="${recommended}" fi elif [[ ! "${min_instances}" =~ ${POSITIVE_INT_REGEX} ]]; then echo " The input must be a positive integer." fi done } prompt_max_instances() { while [[ ! "${max_instances}" =~ ${POSITIVE_INT_REGEX} || \ "${max_instances}" == '?' || \ "${min_instances}" -gt "${max_instances}" ]]; do recommended="6" suggested="$(\ generate_suggested "${cur_max_instances}" "Recommended: ${recommended}")" printf "Maximum Number of Servers (${suggested}): " read max_instances if [[ "${max_instances}" == '?' ]]; then echo "${MAX_INSTANCES_HELP}" elif [[ -z "${max_instances}" ]]; then if [[ ! -z "${cur_max_instances}" ]]; then max_instances="${cur_max_instances}" else max_instances="${recommended}" fi elif [[ ! "${max_instances}" =~ ${POSITIVE_INT_REGEX} ]]; then echo " The input must be a positive integer." elif [[ "${min_instances}" -gt "${max_instances}" ]]; then echo " The input must be equal or greater than the minimum number of servers." fi done } prompt_cpu_target() { while [[ -z "${cpu_target}" || "${cpu_target}" == '?' || \ ! "${cpu_target}" =~ ${BETWEEN_ZERO_AND_ONE_REGEX} ]]; do recommended="0.6" suggested="$(\ generate_suggested "${cur_cpu_target}" "Recommended: ${recommended}")" printf "CPU Target Utilization (${suggested}): " read cpu_target if [[ "${cpu_target}" == '?' ]]; then echo "${CPU_TARGET_HELP}" elif [[ -z "${cpu_target}" ]]; then if [[ ! -z "${cur_cpu_target}" ]]; then cpu_target="${cur_cpu_target}" else cpu_target="${recommended}" fi elif [[ ! "${cpu_target}" =~ ${BETWEEN_ZERO_AND_ONE_REGEX} ]]; then echo " The input must be between 0 and 1." fi done } cur_app_versions="$(gcloud app versions list\ --format="table(service,id,traffic_split,version.servingStatus)" 2> /dev/null )" || true for env in $TEST_ENV $PROD_ENV $PROD1_ENV; do cur_deployment_type="$(\ echo "${cur_app_versions}" | sed -n \ "s/default[[:blank:]]\{1,\}\(${env}\)[[:blank:]]\{1,\}1\.00.*/\1/p")" if [[ ! -z "${cur_deployment_type}" ]]; then break fi done cur_settings="$(\ gcloud app versions describe "${cur_deployment_type}" \ --format=yaml -s default 2> /dev/null)" || true cur_container_config="$(\ extract_from_yaml "${cur_settings}" "CONTAINER_CONFIG")" if gcloud app services list --verbosity=none > /dev/null 2> /dev/null \ && [[ -z "${cur_container_config}" && -z "${cur_policy_script_url}" ]]; then cur_deployment_type='' prompt_continue_default_no "${EXISTING_APP_ENGINE_SERVICE_PROMPT}" echo "" fi cur_policy_script_url="$(\ extract_from_yaml "${cur_settings}" "POLICY_SCRIPT_URL")" if [[ "${cur_deployment_type}" == "${PROD_ENV}"* ]]; then if [[ ! -z "$(echo "${cur_settings}" | sed -n '/automaticScaling/p')" ]]; then cur_autoscaling="on" elif [[ ! -z "$(echo "${cur_settings}" | sed -n '/manualScaling/p')" ]]; then cur_autoscaling="off" fi cur_num_instances="$(\ extract_from_yaml "${cur_settings}" "instances")" cur_min_instances="$(\ extract_from_yaml "${cur_settings}" "minTotalInstances")" cur_max_instances="$(\ extract_from_yaml "${cur_settings}" "maxTotalInstances")" cur_cpu_target="$(\ extract_from_yaml "${cur_settings}" "targetUtilization")" fi request_exclude_filters="$(gcloud -q alpha logging sinks describe "_Default" \ --format text --verbosity=none | sed -n \ -e '/exclusions.*filter:.*appengine.googleapis.com\/nginx.request/{n;p;}' \ -e '/exclusions.*filter:.*appengine.googleapis.com\/request_log/{n;p;}' | sed -n 's/.*name:[ ]*\(.*\)$/\1/p' | sort | uniq | tr '\n' ',')" if [[ ! -z "${request_exclude_filters}" ]]; then log_filters="$(gcloud logging sinks describe "_Default" \ --flatten filter --format list 2>/dev/null | sed 's/^ - //')" gcloud logging sinks update "_Default" --log-filter="$(\ echo "${log_filters}"; echo "${EXCLUDE_REQUEST_LOGS}")" 2> /dev/null gcloud alpha logging sinks update "_Default" \ --remove-exclusions="${request_exclude_filters}" 2> /dev/null fi log_sinks="$(gcloud -q logging sinks describe "_Default" --verbosity=none \ --flatten filter --format list 2>/dev/null)" && logging_enabled=true || logging_enabled=false log_filters="$(echo "${log_sinks}" | sed 's/^ - //')" if [[ "${log_filters}" == *"${EXCLUDE_REQUEST_LOGS}"* || "${logging_enabled}" == "false" ]]; then cur_request_logging="off" else cur_request_logging="on" fi echo "${WELCOME_TEXT}" read -r unused prompt_container_config prompt_policy_script_url prompt_request_logging if [[ "${request_logging}" == "off" && "${cur_request_logging}" != "off" ]]; then log_filters="$(echo "${log_filters}"; echo "${EXCLUDE_REQUEST_LOGS}")" elif [[ "${request_logging}" == "on" && "${cur_request_logging}" != "on" ]]; then log_filters="$(echo "${log_filters}" | sed "/${EXCLUDE_REQUEST_LOGS_ESCAPED}/d")" fi prompt_deployment_type if [[ "${deployment_type}" == "${TEST_ENV}" ]]; then echo "" echo "Your configured settings are" echo "Container Config: ${container_config}" echo "Policy Script URL: ${policy_script_url}" echo "Request Logging: ${request_logging}" echo "Deployment Type: ${deployment_type}" if [[ "${cur_deployment_type}" == "${PROD_ENV}"* ]]; then echo "${DOWNGRADE_NOTE}" fi prompt_continue_default_no "${WISH_TO_CONTINUE}" if [[ "${deployment_type}" == "${cur_deployment_type}" && "${container_config}" == "${cur_container_config}" && "${policy_script_url}" == "${cur_policy_script_url}" ]]; then same_deployment_settings="true" else same_deployment_settings="false" fi if [[ "${same_deployment_settings}" == "true" && "${request_logging}" == "${cur_request_logging}" ]]; then echo "" echo "${SAME_SETTINGS}" prompt_continue_default_no "${WISH_TO_CONTINUE}" fi echo "As you wish." testing_deployment "${container_config}" "${policy_script_url}" fi if [[ "${deployment_type}" == "${PROD_ENV}" ]]; then prompt_autoscaling if [[ "${autoscaling}" == "off" ]]; then prompt_num_instances else prompt_min_instances prompt_max_instances prompt_cpu_target fi echo "" echo "Your configured settings are" echo "Container Config: ${container_config}" echo "Policy Script URL: ${policy_script_url}" echo "Request Logging: ${request_logging}" echo "Deployment Type: ${deployment_type}" echo "Autoscaling: ${autoscaling}" if [[ "${autoscaling}" == "off" ]]; then echo "Number of Servers: ${num_instances}" else echo "Minimum Number of Servers: ${min_instances}" echo "Maximum Number of Servers: ${max_instances}" echo "CPU Target Utilization: ${cpu_target}" fi if [[ "${cur_deployment_type}" == "${TEST_ENV}" ]]; then echo "${UPGRADE_NOTE}" fi prompt_continue_default_no "${WISH_TO_CONTINUE}" if [[ "${cur_deployment_type}" == "${deployment_type}"* && "${container_config}" == "${cur_container_config}" && "${policy_script_url}" == "${cur_policy_script_url}" && "${num_instances}" == "${cur_num_instances}" && "${min_instances}" == "${cur_min_instances}" && "${max_instances}" == "${cur_max_instances}" && "${cpu_target}" == "${cur_cpu_target}" ]]; then same_deployment_settings="true" else same_deployment_settings="false" fi if [[ "${same_deployment_settings}" == "true" && "${request_logging}" == "${cur_request_logging}" ]]; then echo "" echo "${SAME_SETTINGS}" prompt_continue_default_no "${WISH_TO_CONTINUE}" fi echo "As you wish." next_deployment_type="$(\ determine_next_production_type "${cur_deployment_type}")" if [[ "${autoscaling}" == "off" ]]; then production_deployment "${container_config}" "${policy_script_url}" \ "${num_instances}" else next_num_instances=$(("$(gcloud app instances list\ -v "${cur_deployment_type}" --format="value(id)" | wc -l)" + 1)) if [[ "${next_num_instances}" -gt "${max_instances}" ]]; then next_num_instances="${max_instances}" fi production_deployment "${container_config}" "${policy_script_url}" \ "${next_num_instances}" "${max_instances}" "${cpu_target}" fi fi if [[ "${request_logging}" == "on" && "${logging_enabled}" == "false" ]]; then gcloud logging sinks list > /dev/null elif [[ "${request_logging}" != "${cur_request_logging}" ]]; then gcloud logging sinks update "_Default" --log-filter="${log_filters}" fi if [[ "${deployment_type}" == "${PROD_ENV}" && "${autoscaling}" == "on" ]]; then attempt_set_min_instances "${next_deployment_type}" "${min_instances}" wait_all_operations_complete min_instances_set="$(gcloud app versions describe "${next_deployment_type}"\ -s default --format="value(automaticScaling.minTotalInstances)")" if [[ "${min_instances_set}" != "${min_instances}" ]]; then echo "${MIN_INSTANCES_UPDATE_FAILED_NOTE}" next_deployment_type="$(\ determine_next_production_type "${next_deployment_type}")" preview_server_url="$( gcloud app services browse "${DEBUG_SERVER}" \ --format="value(url)" --no-launch-browser)" production_tagging_deployment "${container_config}" "${policy_script_url}" \ "${min_instances}" "${max_instances}" "${cpu_target}" "${preview_server_url}" wait_all_operations_complete fi fi echo "" echo "Your server deployment is complete." exit 0