Skip to content

Commit

Permalink
env/android-amd64-emu: add new Android emulator container + VM image
Browse files Browse the repository at this point in the history
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 <bradfitz@golang.org>
  • Loading branch information
bradfitz committed Feb 28, 2019
1 parent 44b79b8 commit cfe14ab
Show file tree
Hide file tree
Showing 11 changed files with 325 additions and 9 deletions.
54 changes: 53 additions & 1 deletion cmd/buildlet/buildlet.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,15 @@ var (
)

func main() {
switch os.Getenv("GO_BUILDER_ENV") {
builderEnv := os.Getenv("GO_BUILDER_ENV")

switch builderEnv {
case "macstadium_vm":
configureMacStadium()
case "linux-arm-arm5spacemonkey":
initBaseUnixEnv() // Issue 28041
}

onGCE := metadata.OnGCE()
switch runtime.GOOS {
case "plan9":
Expand All @@ -133,6 +136,10 @@ func main() {
log.Printf("buildlet starting.")
flag.Parse()

if builderEnv == "android-amd64-emu" {
startAndroidEmulator()
}

if *reverse == "solaris-amd64-smartosbuildlet" {
// These machines were setup without GO_BUILDER_ENV
// set in their base image, so do init work here after
Expand Down Expand Up @@ -860,6 +867,10 @@ func handleExec(w http.ResponseWriter, r *http.Request) {
return
}
}
if err := checkAndroidEmulator(); err != nil {
http.Error(w, "android emulator not running: "+err.Error(), http.StatusInternalServerError)
return
}

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

Expand Down Expand Up @@ -1799,3 +1810,44 @@ func removeAllAndMkdir(dir string) {
log.Fatal(err)
}
}

var (
androidEmuDead = make(chan error) // closed on death
androidEmuErr error // set prior to channel close
)

func startAndroidEmulator() {
cmd := exec.Command("/android/sdk/emulator/emulator",
"@android-avd",
"-no-audio",
"-no-window",
"-no-boot-anim",
"-no-snapshot-save",
"-wipe-data", // required to prevent a hang with -no-window when recovering from a snapshot?
)
log.Printf("running Android emulator: %v", cmd.Args)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
log.Fatalf("failed to start Android emulator: %v", err)
}
go func() {
err := cmd.Wait()
if err == nil {
err = errors.New("exited without error")
}
androidEmuErr = err
close(androidEmuDead)
}()
}

// checkAndroidEmulator returns an error if this machine is an Android builder
// and the Android emulator process has exited.
func checkAndroidEmulator() error {
select {
case <-androidEmuDead:
return androidEmuErr
default:
return nil
}
}
33 changes: 33 additions & 0 deletions cmd/buildlet/stage0/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Copyright 2017 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.

FROM golang:1.11
LABEL maintainer "golang-dev@googlegroups.com"

ENV CGO_ENABLED=0

# BEGIN deps (run `make update-deps` to update)

# Repo cloud.google.com/go at 5c31045 (2018-05-08)
ENV REV=5c31045bc3f4855c97f997a1940dfefc1598aa2d
RUN go get -d cloud.google.com/go/compute/metadata &&\
(cd /go/src/cloud.google.com/go && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)

# Repo golang.org/x/net at d11bb6c (2018-05-07)
ENV REV=d11bb6cd8e3c4e60239c9cb20ef68586d74500d0
RUN go get -d golang.org/x/net/context `#and 2 other pkgs` &&\
(cd /go/src/golang.org/x/net && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)

# Optimization to speed up iterative development, not necessary for correctness:
RUN go install cloud.google.com/go/compute/metadata \
golang.org/x/net/context \
golang.org/x/net/context/ctxhttp
# END deps.

COPY . /go/src/golang.org/x/build/

# Install static binary to /go/bin/stage0
RUN go install golang.org/x/build/cmd/buildlet/stage0


4 changes: 4 additions & 0 deletions cmd/coordinator/coordinator.go
Original file line number Diff line number Diff line change
Expand Up @@ -1594,6 +1594,10 @@ func (st *buildStatus) expectedBuildletStartDuration() time.Duration {
pool := st.buildletPool()
switch pool.(type) {
case *gceBuildletPool:
if strings.HasPrefix(st.Name, "android-") {
// about a minute for buildlet + minute for Android emulator to be usable
return 2 * time.Minute
}
return time.Minute
case *reverseBuildletPool:
goos, arch := st.conf.GOOS(), st.conf.GOARCH()
Expand Down
7 changes: 5 additions & 2 deletions cmd/xb/xb.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,11 @@ func runDocker() {
case "golang/buildlet-stage0":
log.Printf("building dependent layer %q", layer)
buildStage0Container()
case "debian:stretch":
// TODO: validate version of stretch
case "debian:stretch", "debian:buster":
// TODO: validate version? probably doesn't matter, as they're
// pretty frozen and just get security/bug updates, and most of
// our Dockerfiles start with apt-get update && upgrade steps
// anyway.
default:
log.Fatalf("unsupported layer %q; don't know how to validate or build", layer)
}
Expand Down
52 changes: 47 additions & 5 deletions dashboard/builders.go
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,15 @@ var Hosts = map[string]*HostConfig{
ExpectNum: 1,
env: []string{"GOROOT_BOOTSTRAP=/opt/freeware/lib/golang"},
},
"host-android-amd64-emu": &HostConfig{
Notes: "Debian Buster w/ Android SDK + emulator (use nested virt)",
ContainerImage: "android-amd64-emu:ba38cc4f0c38",
KonletVMImage: "android-amd64-emu",
NestedVirt: true,
buildletURLTmpl: "http://storage.googleapis.com/$BUCKET/buildlet.linux-amd64",
env: []string{"GOROOT_BOOTSTRAP=/go1.4"},
SSHUsername: "root",
},
}

func init() {
Expand Down Expand Up @@ -860,10 +869,13 @@ func (c *BuildConfig) SplitMakeRun() bool {
// These we've verified to work.
return true
}
// TODO(bradfitz): make androidtest.bash and iotest.bash work
// too. And buildall.bash should really just be N small
// container jobs instead of a "buildall.bash". Then we can
// delete this whole method.
if c.GOOS() == "android" && c.HostType == "host-android-amd64-emu" {
return true
}
// TODO(bradfitz): make iotest.bash work too. And
// buildall.bash should really just be N small container jobs
// instead of a "buildall.bash". Then we can delete this whole
// method.
return false
}

Expand All @@ -873,7 +885,9 @@ func (c *BuildConfig) buildSubrepos() bool {
}
// TODO(bradfitz,dmitshur): move this into BuildConfig bools, rather than this Name switch.
switch c.Name {
case "darwin-amd64-10_11",
case "android-amd64-emu",
"android-386-emu",
"darwin-amd64-10_11",
"darwin-386-10_11",
// TODO: add darwin-amd64-10_12 when we have a build scheduler
"freebsd-amd64-93",
Expand Down Expand Up @@ -1731,6 +1745,34 @@ func init() {
"CC_FOR_TARGET=/Users/elias/android-ndk-standalone-arm64/bin/clang",
},
})
addBuilder(BuildConfig{
Name: "android-386-emu",
HostType: "host-android-amd64-emu", // same amd64 host is used for 386 builder
Notes: "Android emulator on GCE",
TryOnly: true, // but not in trybot set for now
tryBot: nil,
env: []string{
"GOARCH=386",
"GOOS=android",
"GOHOSTARCH=amd64",
"GOHOSTOS=linux",
"CGO_ENABLED=1",
},
})
addBuilder(BuildConfig{
Name: "android-amd64-emu",
HostType: "host-android-amd64-emu",
Notes: "Android emulator on GCE",
TryOnly: true, // but not in trybot set for now
tryBot: nil,
env: []string{
"GOARCH=amd64",
"GOOS=android",
"GOHOSTARCH=amd64",
"GOHOSTOS=linux",
"CGO_ENABLED=1",
},
})
addBuilder(BuildConfig{
Name: "android-386-emulator",
HostType: "host-darwin-amd64-eliasnaur-android",
Expand Down
81 changes: 81 additions & 0 deletions env/android-amd64-emu/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Copyright 2019 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.

FROM golang/buildlet-stage0 AS stage0

FROM debian:buster
MAINTAINER golang-dev <golang-dev@googlegroups.com>

ENV DEBIAN_FRONTEND noninteractive

ENV GO_BUILDER_ENV android-amd64-emu
ENV PATH="${PATH}:/android/sdk/platform-tools"
ENV CC_FOR_android_386=/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android26-clang
ENV CC_FOR_android_amd64=/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android26-clang

# gdb: optionally used by runtime tests for gdb
# strace: optionally used by some net/http tests
# gcc libc6-dev: for building Go's bootstrap 'dist' prog
# libc6-dev-i386 gcc-multilib: for 32-bit builds
# procps lsof psmisc: misc basic tools
# libgles2-mesa-dev libopenal-dev fonts-noto: required by x/mobile repo
# unzip openjdk-8-jdk python lib32z1: required by the Android SDK
RUN apt-get update && apt-get install -y \
--no-install-recommends \
ca-certificates \
curl \
gdb \
strace \
gcc \
libc6-dev \
libc6-dev-i386 \
gcc-multilib \
procps \
lsof \
psmisc \
libgles2-mesa-dev \
libopenal-dev \
fonts-noto \
fonts-noto-mono \
openssh-server \
unzip \
openjdk-8-jdk \
python \
lib32z1 \
&& rm -rf /var/lib/apt/lists/*

RUN mkdir -p /go1.4-amd64 \
&& ( \
curl --silent https://storage.googleapis.com/golang/go1.4.linux-amd64.tar.gz | tar -C /go1.4-amd64 -zxv \
) \
&& mv /go1.4-amd64/go /go1.4 \
&& rm -rf /go1.4-amd64 \
&& rm -rf /go1.4/pkg/linux_amd64_race \
/go1.4/api \
/go1.4/blog \
/go1.4/doc \
/go1.4/misc \
/go1.4/test \
&& find /go1.4 -type d -name testdata | xargs rm -rf
RUN mkdir -p /android/sdk \
&& curl -o /android/sdk/sdk-tools-linux.zip https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip \
&& unzip -d /android/sdk /android/sdk/sdk-tools-linux.zip \
&& rm -rf /android/sdk/sdk-tools-linux.zip

RUN yes | /android/sdk/tools/bin/sdkmanager --licenses \
&& /android/sdk/tools/bin/sdkmanager ndk-bundle "system-images;android-26;default;x86_64" \
&& /android/sdk/tools/bin/sdkmanager "build-tools;21.1.2" "platforms;android-26" \
&& /android/sdk/tools/bin/sdkmanager --update

# Gradle for gomobile
RUN curl -L -o /android/gradle-5.2.1-bin.zip https://services.gradle.org/distributions/gradle-5.2.1-bin.zip \
&& unzip -d /android /android/gradle-5.2.1-bin.zip \
&& rm /android/gradle-5.2.1-bin.zip

# Create emulator
RUN echo no | /android/sdk/tools/bin/avdmanager create avd --force --name android-avd --package "system-images;android-26;default;x86_64"

COPY --from=stage0 /go/bin/stage0 /usr/local/bin/stage0

CMD ["/usr/local/bin/stage0"]
27 changes: 27 additions & 0 deletions env/android-amd64-emu/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Copyright 2018 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.

IMAGE_NAME=$(shell basename $(CURDIR))

usage:
echo "see Makefile for usage for building $(IMAGE_NAME)"

docker: Dockerfile
go install golang.org/x/build/cmd/xb
xb docker build -t golang/$(IMAGE_NAME) .

push-staging: docker
go install golang.org/x/build/cmd/xb
$(eval REV := $(shell docker images --format={{.ID}} golang/android-amd64-emu:latest))
xb --staging docker tag golang/$(IMAGE_NAME) REPO/$(IMAGE_NAME):$(REV)
xb --staging docker push REPO/$(IMAGE_NAME):$(REV)

push-prod: docker
go install golang.org/x/build/cmd/xb
$(eval REV := $(shell docker images --format={{.ID}} golang/android-amd64-emu:latest))
xb --prod docker tag golang/$(IMAGE_NAME) REPO/$(IMAGE_NAME):$(REV)
xb --prod docker push REPO/$(IMAGE_NAME):$(REV)

vm-prod: push-prod
./create-vm.sh
6 changes: 6 additions & 0 deletions env/android-amd64-emu/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
The android-amd64-emu builder is built in two parts:

* first, a large Docker container
* second, a VMX+konlet-based VM image with that Docker image pre-pulled


Loading

0 comments on commit cfe14ab

Please sign in to comment.