1#!/bin/bash
2
3# Copyright 2018 Google Inc. All rights reserved.
4
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8
9#     http://www.apache.org/licenses/LICENSE-2.0
10
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17# Common code to build a host image on GCE
18
19# INTERNAL_IP can be set to --internal-ip run on a GCE instance
20# The instance will need --scope compute-rw
21
22source "${ANDROID_BUILD_TOP}/external/shflags/shflags"
23
24DEFINE_string build_instance \
25  "${USER}-build" "Instance name to create for the build" "i"
26DEFINE_string build_project "$(gcloud config get-value project)" \
27  "Project to use for scratch"
28DEFINE_string build_zone "$(gcloud config get-value compute/zone)" \
29  "Zone to use for scratch resources"
30DEFINE_string dest_image "vsoc-host-scratch-${USER}" "Image to create" "o"
31DEFINE_string dest_family "" "Image family to add the image to" "f"
32DEFINE_string dest_project "$(gcloud config get-value project)" \
33  "Project to use for the new image" "p"
34DEFINE_string launch_instance "" \
35  "Name of the instance to launch with the new image" "l"
36DEFINE_string source_image_family "debian-11" \
37  "Image familty to use as the base" "s"
38DEFINE_string source_image_project debian-cloud \
39  "Project holding the base image" "m"
40DEFINE_string repository_url \
41  "https://github.com/google/android-cuttlefish.git" \
42  "URL to the repository with host changes" "u"
43DEFINE_string repository_branch main \
44  "Branch to check out" "b"
45
46
47SSH_FLAGS=(${INTERNAL_IP})
48
49fatal_echo() {
50  echo "$1"
51  exit 1
52}
53
54wait_for_instance() {
55  alive=""
56  while [[ -z "${alive}" ]]; do
57    sleep 5
58    alive="$(gcloud compute ssh "${SSH_FLAGS[@]}" "$@" -- uptime || true)"
59  done
60}
61
62package_source() {
63  local url="$1"
64  local branch="$2"
65  local repository_dir="${url/*\//}"
66  repository_dir="$(basename "${repository_dir}" .git)"
67  local debian_dir="${repository_dir}"
68  if [[ $# -eq 4 ]]; then
69    debian_dir="${repository_dir}/$4"
70  fi
71  git clone "${url}" -b "${branch}"
72  dpkg-source -b "${debian_dir}"
73  rm -rf "${repository_dir}"
74}
75
76main() {
77  set -o errexit
78  set -x
79  PZ=(--project=${FLAGS_build_project} --zone=${FLAGS_build_zone})
80  if [[ -n "${FLAGS_dest_family}" ]]; then
81    dest_family_flag=("--family=${FLAGS_dest_family}")
82  else
83    dest_family_flag=()
84  fi
85  scratch_dir="$(mktemp -d)"
86  pushd "${scratch_dir}"
87  package_source "${FLAGS_repository_url}" "${FLAGS_repository_branch}" \
88    "cuttlefish-common_${FLAGS_version}" "base"
89  package_source "${FLAGS_repository_url}" "${FLAGS_repository_branch}" \
90    "cuttlefish-frontend_${FLAGS_version}" "frontend"
91  popd
92  source_files=(
93    "${ANDROID_BUILD_TOP}/device/google/cuttlefish/tools/create_base_image_gce.sh"
94    ${scratch_dir}/*
95  )
96
97  delete_instances=("${FLAGS_build_instance}" "${FLAGS_dest_image}")
98  if [[ -n "${FLAGS_launch_instance}" ]]; then
99    delete_instances+=("${FLAGS_launch_instance}")
100  fi
101  gcloud compute instances delete -q \
102    "${PZ[@]}" "${delete_instances[@]}" || \
103      echo Not running
104  gcloud compute disks delete -q \
105    "${PZ[@]}" "${FLAGS_dest_image}" || echo No scratch disk
106  gcloud compute images delete -q \
107    --project="${FLAGS_build_project}" "${FLAGS_dest_image}" || echo Not respinning
108  gcloud compute disks create \
109    "${PZ[@]}" \
110    --image-family="${FLAGS_source_image_family}" \
111    --image-project="${FLAGS_source_image_project}" \
112    "${FLAGS_dest_image}"
113  local gpu_type="nvidia-tesla-p100-vws"
114  gcloud compute accelerator-types describe "${gpu_type}" "${PZ[@]}" || \
115    fatal_echo "Please use a zone with ${gpu_type} GPUs available."
116  gcloud compute instances create \
117    "${PZ[@]}" \
118    --machine-type=n1-standard-16 \
119    --image-family="${FLAGS_source_image_family}" \
120    --image-project="${FLAGS_source_image_project}" \
121    --boot-disk-size=200GiB \
122    --accelerator="type=${gpu_type},count=1" \
123    --maintenance-policy=TERMINATE \
124    "${FLAGS_build_instance}"
125  wait_for_instance "${PZ[@]}" "${FLAGS_build_instance}"
126  # Ubuntu tends to mount the wrong disk as root, so help it by waiting until
127  # it has booted before giving it access to the clean image disk
128  gcloud compute instances attach-disk \
129      "${PZ[@]}" \
130      "${FLAGS_build_instance}" --disk="${FLAGS_dest_image}"
131  # beta for the --internal-ip flag that may be passed via SSH_FLAGS
132  gcloud beta compute scp "${SSH_FLAGS[@]}" "${PZ[@]}" \
133    "${source_files[@]}" \
134    "${FLAGS_build_instance}:"
135  gcloud compute ssh "${SSH_FLAGS[@]}" \
136    "${PZ[@]}" "${FLAGS_build_instance}" -- \
137    ./create_base_image_gce.sh
138  gcloud compute instances delete -q \
139    "${PZ[@]}" "${FLAGS_build_instance}"
140  gcloud compute images create \
141    --project="${FLAGS_build_project}" \
142    --source-disk="${FLAGS_dest_image}" \
143    --source-disk-zone="${FLAGS_build_zone}" \
144    --licenses=https://www.googleapis.com/compute/v1/projects/vm-options/global/licenses/enable-vmx \
145    "${dest_family_flag[@]}" \
146    "${FLAGS_dest_image}"
147  gcloud compute disks delete -q "${PZ[@]}" \
148    "${FLAGS_dest_image}"
149  if [[ -n "${FLAGS_launch_instance}" ]]; then
150    gcloud compute instances create "${PZ[@]}" \
151      --image-project="${FLAGS_build_project}" \
152      --image="${FLAGS_dest_image}" \
153      --machine-type=n1-standard-4 \
154      --scopes storage-ro \
155      --accelerator="type=${gpu_type},count=1" \
156      --maintenance-policy=TERMINATE \
157      --boot-disk-size=200GiB \
158      "${FLAGS_launch_instance}"
159  fi
160  cat <<EOF
161    echo Test and if this looks good, consider releasing it via:
162
163    gcloud compute images create \
164      --project="${FLAGS_dest_project}" \
165      --source-image="${FLAGS_dest_image}" \
166      --source-image-project="${FLAGS_build_project}" \
167      "${dest_family_flag[@]}" \
168      "${FLAGS_dest_image}"
169EOF
170}
171
172FLAGS "$@" || exit 1
173if [[ "${FLAGS_help}" -eq 0 ]]; then
174  echo ${FLAGS_help}
175  exit 1
176fi
177
178main "${FLAGS_ARGV[@]}"
179