blob: c971cfe9b620d9ec5f6fed3105ab535d5de3872c [file] [log] [blame]
#!/bin/bash
# Copyright 2020 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
FACTORY_DIR="$(readlink -f "${SCRIPT_DIR}/..")"
FACTORY_PRIVATE_DIR="${FACTORY_DIR}/../factory-private"
LOCAL_BUILD_BUNDLE="${FACTORY_DIR}/build/bundle"
TOOLKIT_NAME="install_factory_toolkit.run"
LOCAL_BUILD_TOOLKIT="${LOCAL_BUILD_BUNDLE}/toolkit/${TOOLKIT_NAME}"
SOURCE_DIR="${FACTORY_DIR}/py/bundle_creator/"
. "${FACTORY_DIR}/devtools/mk/common.sh" || exit 1
. "${FACTORY_PRIVATE_DIR}/config/bundle_creator/config.sh" || exit 1
# Following variables will be assigned by `load_config <DEPLOYMENT_TYPE>`
GCLOUD_PROJECT=
DOCKER_IMAGENAME=
CONTAINER_IMAGE=
INSTANCE_TEMPLATE_NAME=
INSTANCE_GROUP_NAME=
BUNDLE_BUCKET=
PUBSUB_TOPIC=
PUBSUB_SUBSCRIPTION=
ALLOWED_LOAS_PEER_USERNAMES=
NOREPLY_EMAIL=
FAILURE_EMAIL=
load_config_by_deployment_type() {
local deployment_type="$1"
if ! load_config "${deployment_type}"; then
die "Unsupported deployment type: \"${deployment_type}\"."
fi
}
build_docker() {
load_config_by_deployment_type "$1"
local temp_dir="$(mktemp -d)"
if [ ! -d "${temp_dir}" ]; then
die "Failed to create a temporary placeholder for files to deploy."
fi
add_temp "${temp_dir}"
rsync -avr --exclude="app_engine*" --exclude="proto" "${SOURCE_DIR}"/* \
"${temp_dir}"
cp "${FACTORY_PRIVATE_DIR}/config/bundle_creator/service_account.json" \
"${temp_dir}/docker"
if [ -f "${LOCAL_BUILD_TOOLKIT}" ]; then
cp "${LOCAL_BUILD_TOOLKIT}" "${temp_dir}/docker"
cp -rf "${LOCAL_BUILD_BUNDLE}/setup" "${temp_dir}/docker"
else
cp -f "/bin/false" "${temp_dir}/docker/${TOOLKIT_NAME}"
mkdir -p "${temp_dir}/docker/setup"
fi
# Fill in env vars in docker/config.py
env GCLOUD_PROJECT="${GCLOUD_PROJECT}" \
BUNDLE_BUCKET="${BUNDLE_BUCKET}" \
PUBSUB_SUBSCRIPTION="${PUBSUB_SUBSCRIPTION}" \
envsubst < "${SOURCE_DIR}/docker/config.py" > "${temp_dir}/docker/config.py"
protoc -I "${SOURCE_DIR}/proto/" --python_out "${temp_dir}/docker" \
"${SOURCE_DIR}/proto/factorybundle.proto"
docker build -t "${DOCKER_IMAGENAME}" --file "${temp_dir}/docker/Dockerfile" \
"${temp_dir}"
}
deploy_docker() {
load_config_by_deployment_type "$1"
gcloud --project="${GCLOUD_PROJECT}" docker -- push "${DOCKER_IMAGENAME}"
gcloud --project="${GCLOUD_PROJECT}" compute project-info \
add-metadata --metadata bundle-creator-docker="${DOCKER_IMAGENAME}"
}
deploy_appengine() {
load_config_by_deployment_type "$1"
local temp_dir="$(mktemp -d)"
if [ ! -d "${temp_dir}" ]; then
die "Failed to create a temporary placeholder for files to deploy."
fi
add_temp "${temp_dir}"
local factory_dir="${temp_dir}/cros/factory"
local package_dir="${factory_dir}/bundle_creator"
mkdir -p "${package_dir}"
cp -r "${SOURCE_DIR}/app_engine" "${package_dir}"
cp -r "${SOURCE_DIR}/connector" "${package_dir}"
local allowed_array=$(printf ", \'%s\'" "${ALLOWED_LOAS_PEER_USERNAMES[@]}")
allowed_array="${allowed_array:3:$((${#allowed_array}-4))}"
# Fill in env vars in rpc/config.py
env GCLOUD_PROJECT="${GCLOUD_PROJECT}" \
BUNDLE_BUCKET="${BUNDLE_BUCKET}" \
PUBSUB_TOPIC="${PUBSUB_TOPIC}" \
ALLOWED_LOAS_PEER_USERNAMES="${allowed_array}" \
NOREPLY_EMAIL="${NOREPLY_EMAIL}" \
FAILURE_EMAIL="${FAILURE_EMAIL}" \
envsubst < "${SOURCE_DIR}/app_engine/config.py" \
> "${package_dir}/app_engine/config.py"
mv "${package_dir}/app_engine/app.yaml" "${temp_dir}"
mv "${package_dir}/app_engine/requirements.txt" "${temp_dir}"
protoc --python_out="${package_dir}/app_engine" -I "${SOURCE_DIR}/proto" \
"${SOURCE_DIR}/proto/factorybundle.proto"
gcloud --project="${GCLOUD_PROJECT}" app deploy \
"${temp_dir}/app.yaml" --quiet
}
deploy_appengine_legacy() {
load_config_by_deployment_type "$1"
local temp_dir=$(mktemp -d)
if [ ! -d "${temp_dir}" ]; then
die "Failed to create a temporary placeholder for files to deploy."
fi
add_temp "${temp_dir}"
cp -r "${SOURCE_DIR}"/app_engine_legacy/* "${temp_dir}"
# Fill in env vars in rpc/config.py
env BUNDLE_BUCKET="${BUNDLE_BUCKET}" \
NOREPLY_EMAIL="${NOREPLY_EMAIL}" \
FAILURE_EMAIL="${FAILURE_EMAIL}" \
envsubst < "${SOURCE_DIR}/app_engine_legacy/rpc/config.py" \
> "${temp_dir}/rpc/config.py"
protoc -o "${temp_dir}/rpc/factorybundle.proto.def" \
-I "${SOURCE_DIR}" "${SOURCE_DIR}/proto/factorybundle.proto"
gcloud --project="${GCLOUD_PROJECT}" app deploy \
"${temp_dir}/app.yaml" --quiet
gcloud --project="${GCLOUD_PROJECT}" app deploy \
"${temp_dir}/queue.yaml" --quiet
}
create_vm() {
load_config_by_deployment_type "$1"
{
gcloud beta compute instance-templates --project="${GCLOUD_PROJECT}" \
create-with-container "${INSTANCE_TEMPLATE_NAME}" \
--machine-type=custom-8-16384 \
--network-tier=PREMIUM --maintenance-policy=MIGRATE \
--image=cos-stable-63-10032-71-0 --image-project=cos-cloud \
--boot-disk-size=200GB --boot-disk-type=pd-standard \
--boot-disk-device-name="${INSTANCE_TEMPLATE_NAME}" \
--container-image="${CONTAINER_IMAGE}" \
--container-restart-policy=always --container-privileged \
--labels=container-vm=cos-stable-63-10032-71-0
} || {
# The vm instance is managed by the instance group, we usually delete a vm
# instance by deleting the instance group. And we won't delete the instance
# template once it is created. So output the message to ignore the error
# message from the creating template command.
echo "The specific instance template was created."
}
local zone
zone="us-central1-a"
local filter
filter="zone:${zone} name:${INSTANCE_GROUP_NAME}"
{
gcloud compute instance-groups managed list --project "${GCLOUD_PROJECT}" \
--filter="${filter}" | grep "${INSTANCE_GROUP_NAME}"
} && {
gcloud compute instance-groups managed delete "${INSTANCE_GROUP_NAME}" \
--project "${GCLOUD_PROJECT}" \
--zone "${zone}" \
--quiet
}
gcloud compute instance-groups managed create "${INSTANCE_GROUP_NAME}" \
--project "${GCLOUD_PROJECT}" \
--template "${INSTANCE_TEMPLATE_NAME}" \
--zone "${zone}" \
--size 1
}
deploy_vm() {
build_docker "$1"
deploy_docker "$1"
create_vm "$1"
}
print_usage() {
cat << __EOF__
Easy Bundle Creation Service Deployment Script
commands:
$0 build-docker [prod|staging]
Build the image from the \`Dockerfile\` located at
\`factory/py/bundle_creator/docker/Dockerfile\`.
$0 deploy [prod|staging]
Do \`deploy-appengine\`, \`deploy-appengine-legacy\` and \`deploy-vm\`
commands.
$0 deploy-docker [prod|staging]
Push the docker image built from the command \`build-docker\` to the
Container Registry.
$0 deploy-appengine [prod|staging]
Deploy the code and configuration under
\`factory/py/bundle_creator/app_engine\` to App Engine.
$0 deploy-appengine-legacy [prod|staging]
Deploy the code and configuration under
\`factory/py/bundle_creator/app_engine_legacy\` to App Engine.
$0 create-vm [prod|staging]
Create a compute engine instance which use the docker image deployed by
the command \`deploy-docker\`.
$0 deploy-vm [prod|staging]
Do \`build-docker\`, \`deploy-docker\` and \`create-vm\` commands.
__EOF__
}
main() {
local subcmd="$1"
if [ "${subcmd}" == "help" ]; then
print_usage
else
case "${subcmd}" in
build-docker)
build_docker "$2"
;;
deploy)
deploy_appengine "$2"
deploy_appengine_legacy "$2"
deploy_vm "$2"
;;
deploy-docker)
deploy_docker "$2"
;;
deploy-appengine)
deploy_appengine "$2"
;;
deploy-appengine-legacy)
deploy_appengine_legacy "$2"
;;
create-vm)
create_vm "$2"
;;
deploy-vm)
deploy_vm "$2"
;;
*)
die "Unknown sub-command: \"${subcmd}\". Run \`${0} help\` to print" \
"the usage."
;;
esac
fi
mk_success
}
main "$@"