Skip to content

Commit cfe14ab

Browse files
committed
env/android-amd64-emu: add new Android emulator container + VM image
Now that we can do nested virtualization on GCE, that means we can run the Android emulator (which requires KVM) on GCE and at least get fast trybots and such for android-amd64. Updates golang/go#23824 Change-Id: I0da38c7fa0f15492230a31291d2921ba72f2151d Reviewed-on: https://go-review.googlesource.com/c/163738 Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent 44b79b8 commit cfe14ab

File tree

11 files changed

+325
-9
lines changed

11 files changed

+325
-9
lines changed

cmd/buildlet/buildlet.go

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,15 @@ var (
108108
)
109109

110110
func main() {
111-
switch os.Getenv("GO_BUILDER_ENV") {
111+
builderEnv := os.Getenv("GO_BUILDER_ENV")
112+
113+
switch builderEnv {
112114
case "macstadium_vm":
113115
configureMacStadium()
114116
case "linux-arm-arm5spacemonkey":
115117
initBaseUnixEnv() // Issue 28041
116118
}
119+
117120
onGCE := metadata.OnGCE()
118121
switch runtime.GOOS {
119122
case "plan9":
@@ -133,6 +136,10 @@ func main() {
133136
log.Printf("buildlet starting.")
134137
flag.Parse()
135138

139+
if builderEnv == "android-amd64-emu" {
140+
startAndroidEmulator()
141+
}
142+
136143
if *reverse == "solaris-amd64-smartosbuildlet" {
137144
// These machines were setup without GO_BUILDER_ENV
138145
// set in their base image, so do init work here after
@@ -860,6 +867,10 @@ func handleExec(w http.ResponseWriter, r *http.Request) {
860867
return
861868
}
862869
}
870+
if err := checkAndroidEmulator(); err != nil {
871+
http.Error(w, "android emulator not running: "+err.Error(), http.StatusInternalServerError)
872+
return
873+
}
863874

864875
w.Header().Set("Trailer", hdrProcessState) // declare it so we can set it
865876

@@ -1799,3 +1810,44 @@ func removeAllAndMkdir(dir string) {
17991810
log.Fatal(err)
18001811
}
18011812
}
1813+
1814+
var (
1815+
androidEmuDead = make(chan error) // closed on death
1816+
androidEmuErr error // set prior to channel close
1817+
)
1818+
1819+
func startAndroidEmulator() {
1820+
cmd := exec.Command("/android/sdk/emulator/emulator",
1821+
"@android-avd",
1822+
"-no-audio",
1823+
"-no-window",
1824+
"-no-boot-anim",
1825+
"-no-snapshot-save",
1826+
"-wipe-data", // required to prevent a hang with -no-window when recovering from a snapshot?
1827+
)
1828+
log.Printf("running Android emulator: %v", cmd.Args)
1829+
cmd.Stdout = os.Stdout
1830+
cmd.Stderr = os.Stderr
1831+
if err := cmd.Start(); err != nil {
1832+
log.Fatalf("failed to start Android emulator: %v", err)
1833+
}
1834+
go func() {
1835+
err := cmd.Wait()
1836+
if err == nil {
1837+
err = errors.New("exited without error")
1838+
}
1839+
androidEmuErr = err
1840+
close(androidEmuDead)
1841+
}()
1842+
}
1843+
1844+
// checkAndroidEmulator returns an error if this machine is an Android builder
1845+
// and the Android emulator process has exited.
1846+
func checkAndroidEmulator() error {
1847+
select {
1848+
case <-androidEmuDead:
1849+
return androidEmuErr
1850+
default:
1851+
return nil
1852+
}
1853+
}

cmd/buildlet/stage0/Dockerfile

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Copyright 2017 The Go Authors. All rights reserved.
2+
# Use of this source code is governed by a BSD-style
3+
# license that can be found in the LICENSE file.
4+
5+
FROM golang:1.11
6+
LABEL maintainer "[email protected]"
7+
8+
ENV CGO_ENABLED=0
9+
10+
# BEGIN deps (run `make update-deps` to update)
11+
12+
# Repo cloud.google.com/go at 5c31045 (2018-05-08)
13+
ENV REV=5c31045bc3f4855c97f997a1940dfefc1598aa2d
14+
RUN go get -d cloud.google.com/go/compute/metadata &&\
15+
(cd /go/src/cloud.google.com/go && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
16+
17+
# Repo golang.org/x/net at d11bb6c (2018-05-07)
18+
ENV REV=d11bb6cd8e3c4e60239c9cb20ef68586d74500d0
19+
RUN go get -d golang.org/x/net/context `#and 2 other pkgs` &&\
20+
(cd /go/src/golang.org/x/net && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
21+
22+
# Optimization to speed up iterative development, not necessary for correctness:
23+
RUN go install cloud.google.com/go/compute/metadata \
24+
golang.org/x/net/context \
25+
golang.org/x/net/context/ctxhttp
26+
# END deps.
27+
28+
COPY . /go/src/golang.org/x/build/
29+
30+
# Install static binary to /go/bin/stage0
31+
RUN go install golang.org/x/build/cmd/buildlet/stage0
32+
33+

cmd/coordinator/coordinator.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1594,6 +1594,10 @@ func (st *buildStatus) expectedBuildletStartDuration() time.Duration {
15941594
pool := st.buildletPool()
15951595
switch pool.(type) {
15961596
case *gceBuildletPool:
1597+
if strings.HasPrefix(st.Name, "android-") {
1598+
// about a minute for buildlet + minute for Android emulator to be usable
1599+
return 2 * time.Minute
1600+
}
15971601
return time.Minute
15981602
case *reverseBuildletPool:
15991603
goos, arch := st.conf.GOOS(), st.conf.GOARCH()

cmd/xb/xb.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,11 @@ func runDocker() {
147147
case "golang/buildlet-stage0":
148148
log.Printf("building dependent layer %q", layer)
149149
buildStage0Container()
150-
case "debian:stretch":
151-
// TODO: validate version of stretch
150+
case "debian:stretch", "debian:buster":
151+
// TODO: validate version? probably doesn't matter, as they're
152+
// pretty frozen and just get security/bug updates, and most of
153+
// our Dockerfiles start with apt-get update && upgrade steps
154+
// anyway.
152155
default:
153156
log.Fatalf("unsupported layer %q; don't know how to validate or build", layer)
154157
}

dashboard/builders.go

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,15 @@ var Hosts = map[string]*HostConfig{
530530
ExpectNum: 1,
531531
env: []string{"GOROOT_BOOTSTRAP=/opt/freeware/lib/golang"},
532532
},
533+
"host-android-amd64-emu": &HostConfig{
534+
Notes: "Debian Buster w/ Android SDK + emulator (use nested virt)",
535+
ContainerImage: "android-amd64-emu:ba38cc4f0c38",
536+
KonletVMImage: "android-amd64-emu",
537+
NestedVirt: true,
538+
buildletURLTmpl: "http://storage.googleapis.com/$BUCKET/buildlet.linux-amd64",
539+
env: []string{"GOROOT_BOOTSTRAP=/go1.4"},
540+
SSHUsername: "root",
541+
},
533542
}
534543

535544
func init() {
@@ -860,10 +869,13 @@ func (c *BuildConfig) SplitMakeRun() bool {
860869
// These we've verified to work.
861870
return true
862871
}
863-
// TODO(bradfitz): make androidtest.bash and iotest.bash work
864-
// too. And buildall.bash should really just be N small
865-
// container jobs instead of a "buildall.bash". Then we can
866-
// delete this whole method.
872+
if c.GOOS() == "android" && c.HostType == "host-android-amd64-emu" {
873+
return true
874+
}
875+
// TODO(bradfitz): make iotest.bash work too. And
876+
// buildall.bash should really just be N small container jobs
877+
// instead of a "buildall.bash". Then we can delete this whole
878+
// method.
867879
return false
868880
}
869881

@@ -873,7 +885,9 @@ func (c *BuildConfig) buildSubrepos() bool {
873885
}
874886
// TODO(bradfitz,dmitshur): move this into BuildConfig bools, rather than this Name switch.
875887
switch c.Name {
876-
case "darwin-amd64-10_11",
888+
case "android-amd64-emu",
889+
"android-386-emu",
890+
"darwin-amd64-10_11",
877891
"darwin-386-10_11",
878892
// TODO: add darwin-amd64-10_12 when we have a build scheduler
879893
"freebsd-amd64-93",
@@ -1731,6 +1745,34 @@ func init() {
17311745
"CC_FOR_TARGET=/Users/elias/android-ndk-standalone-arm64/bin/clang",
17321746
},
17331747
})
1748+
addBuilder(BuildConfig{
1749+
Name: "android-386-emu",
1750+
HostType: "host-android-amd64-emu", // same amd64 host is used for 386 builder
1751+
Notes: "Android emulator on GCE",
1752+
TryOnly: true, // but not in trybot set for now
1753+
tryBot: nil,
1754+
env: []string{
1755+
"GOARCH=386",
1756+
"GOOS=android",
1757+
"GOHOSTARCH=amd64",
1758+
"GOHOSTOS=linux",
1759+
"CGO_ENABLED=1",
1760+
},
1761+
})
1762+
addBuilder(BuildConfig{
1763+
Name: "android-amd64-emu",
1764+
HostType: "host-android-amd64-emu",
1765+
Notes: "Android emulator on GCE",
1766+
TryOnly: true, // but not in trybot set for now
1767+
tryBot: nil,
1768+
env: []string{
1769+
"GOARCH=amd64",
1770+
"GOOS=android",
1771+
"GOHOSTARCH=amd64",
1772+
"GOHOSTOS=linux",
1773+
"CGO_ENABLED=1",
1774+
},
1775+
})
17341776
addBuilder(BuildConfig{
17351777
Name: "android-386-emulator",
17361778
HostType: "host-darwin-amd64-eliasnaur-android",

env/android-amd64-emu/Dockerfile

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Copyright 2019 The Go Authors. All rights reserved.
2+
# Use of this source code is governed by a BSD-style
3+
# license that can be found in the LICENSE file.
4+
5+
FROM golang/buildlet-stage0 AS stage0
6+
7+
FROM debian:buster
8+
MAINTAINER golang-dev <[email protected]>
9+
10+
ENV DEBIAN_FRONTEND noninteractive
11+
12+
ENV GO_BUILDER_ENV android-amd64-emu
13+
ENV PATH="${PATH}:/android/sdk/platform-tools"
14+
ENV CC_FOR_android_386=/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android26-clang
15+
ENV CC_FOR_android_amd64=/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android26-clang
16+
17+
# gdb: optionally used by runtime tests for gdb
18+
# strace: optionally used by some net/http tests
19+
# gcc libc6-dev: for building Go's bootstrap 'dist' prog
20+
# libc6-dev-i386 gcc-multilib: for 32-bit builds
21+
# procps lsof psmisc: misc basic tools
22+
# libgles2-mesa-dev libopenal-dev fonts-noto: required by x/mobile repo
23+
# unzip openjdk-8-jdk python lib32z1: required by the Android SDK
24+
RUN apt-get update && apt-get install -y \
25+
--no-install-recommends \
26+
ca-certificates \
27+
curl \
28+
gdb \
29+
strace \
30+
gcc \
31+
libc6-dev \
32+
libc6-dev-i386 \
33+
gcc-multilib \
34+
procps \
35+
lsof \
36+
psmisc \
37+
libgles2-mesa-dev \
38+
libopenal-dev \
39+
fonts-noto \
40+
fonts-noto-mono \
41+
openssh-server \
42+
unzip \
43+
openjdk-8-jdk \
44+
python \
45+
lib32z1 \
46+
&& rm -rf /var/lib/apt/lists/*
47+
48+
RUN mkdir -p /go1.4-amd64 \
49+
&& ( \
50+
curl --silent https://storage.googleapis.com/golang/go1.4.linux-amd64.tar.gz | tar -C /go1.4-amd64 -zxv \
51+
) \
52+
&& mv /go1.4-amd64/go /go1.4 \
53+
&& rm -rf /go1.4-amd64 \
54+
&& rm -rf /go1.4/pkg/linux_amd64_race \
55+
/go1.4/api \
56+
/go1.4/blog \
57+
/go1.4/doc \
58+
/go1.4/misc \
59+
/go1.4/test \
60+
&& find /go1.4 -type d -name testdata | xargs rm -rf
61+
RUN mkdir -p /android/sdk \
62+
&& curl -o /android/sdk/sdk-tools-linux.zip https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip \
63+
&& unzip -d /android/sdk /android/sdk/sdk-tools-linux.zip \
64+
&& rm -rf /android/sdk/sdk-tools-linux.zip
65+
66+
RUN yes | /android/sdk/tools/bin/sdkmanager --licenses \
67+
&& /android/sdk/tools/bin/sdkmanager ndk-bundle "system-images;android-26;default;x86_64" \
68+
&& /android/sdk/tools/bin/sdkmanager "build-tools;21.1.2" "platforms;android-26" \
69+
&& /android/sdk/tools/bin/sdkmanager --update
70+
71+
# Gradle for gomobile
72+
RUN curl -L -o /android/gradle-5.2.1-bin.zip https://services.gradle.org/distributions/gradle-5.2.1-bin.zip \
73+
&& unzip -d /android /android/gradle-5.2.1-bin.zip \
74+
&& rm /android/gradle-5.2.1-bin.zip
75+
76+
# Create emulator
77+
RUN echo no | /android/sdk/tools/bin/avdmanager create avd --force --name android-avd --package "system-images;android-26;default;x86_64"
78+
79+
COPY --from=stage0 /go/bin/stage0 /usr/local/bin/stage0
80+
81+
CMD ["/usr/local/bin/stage0"]

env/android-amd64-emu/Makefile

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Copyright 2018 The Go Authors. All rights reserved.
2+
# Use of this source code is governed by a BSD-style
3+
# license that can be found in the LICENSE file.
4+
5+
IMAGE_NAME=$(shell basename $(CURDIR))
6+
7+
usage:
8+
echo "see Makefile for usage for building $(IMAGE_NAME)"
9+
10+
docker: Dockerfile
11+
go install golang.org/x/build/cmd/xb
12+
xb docker build -t golang/$(IMAGE_NAME) .
13+
14+
push-staging: docker
15+
go install golang.org/x/build/cmd/xb
16+
$(eval REV := $(shell docker images --format={{.ID}} golang/android-amd64-emu:latest))
17+
xb --staging docker tag golang/$(IMAGE_NAME) REPO/$(IMAGE_NAME):$(REV)
18+
xb --staging docker push REPO/$(IMAGE_NAME):$(REV)
19+
20+
push-prod: docker
21+
go install golang.org/x/build/cmd/xb
22+
$(eval REV := $(shell docker images --format={{.ID}} golang/android-amd64-emu:latest))
23+
xb --prod docker tag golang/$(IMAGE_NAME) REPO/$(IMAGE_NAME):$(REV)
24+
xb --prod docker push REPO/$(IMAGE_NAME):$(REV)
25+
26+
vm-prod: push-prod
27+
./create-vm.sh

env/android-amd64-emu/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
The android-amd64-emu builder is built in two parts:
2+
3+
* first, a large Docker container
4+
* second, a VMX+konlet-based VM image with that Docker image pre-pulled
5+
6+

0 commit comments

Comments
 (0)