Skip to content

Commit 46ecb43

Browse files
committed
cmd/coordinator: cross-compile ARM build on Kubernetes
Saves 4.5 minutes or so by using fast x86 machines to build the ARM build instead of running make.bash on Scaleway ARM machines. We still run the tests on ARM, and have a separate builder only running make.bash on ARM (see prior golang.org/cl/29670) Fixes golang/go#17105 Updates golang/go#17104 Change-Id: I1cb7b0e5b1cc8b644195f262328884ed3aff120a Reviewed-on: https://go-review.googlesource.com/29677 Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent 5a057a2 commit 46ecb43

File tree

2 files changed

+142
-20
lines changed

2 files changed

+142
-20
lines changed

cmd/coordinator/coordinator.go

+135-20
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,7 @@ func handleLogs(w http.ResponseWriter, r *http.Request) {
560560
return
561561
}
562562

563-
if !st.hasEvent("make_and_test") {
563+
if !st.hasEvent("make_and_test") && !st.hasEvent("make_cross_compile_kube") {
564564
fmt.Fprintf(w, "\n\n(buildlet still starting; no live streaming. reload manually to see status)\n")
565565
return
566566
}
@@ -633,7 +633,7 @@ func findWorkLoop(work chan<- builderRev) {
633633
//work <- builderRev{name: "linux-arm", rev: "c9778ec302b2e0e0d6027e1e0fca892e428d9657", subName: "tools", subRev: "ac303766f5f240c1796eeea3dc9bf34f1261aa35"}
634634
const debugArm = false
635635
if debugArm {
636-
for !reversePool.CanBuild("buildlet-linux-arm") {
636+
for !reversePool.CanBuild("host-linux-arm") {
637637
log.Printf("waiting for ARM to register.")
638638
time.Sleep(time.Second)
639639
}
@@ -1392,10 +1392,43 @@ func (st *buildStatus) useSnapshot() bool {
13921392
return b
13931393
}
13941394

1395+
func (st *buildStatus) forceSnapshotUsage() {
1396+
st.mu.Lock()
1397+
defer st.mu.Unlock()
1398+
truth := true
1399+
st.useSnapshotMemo = &truth
1400+
}
1401+
1402+
func (st *buildStatus) shouldCrossCompileMake() bool {
1403+
if inStaging {
1404+
return st.name == "linux-arm" && kubeErr == nil
1405+
}
1406+
return st.isTry() && st.name == "linux-arm" && kubeErr == nil
1407+
}
1408+
13951409
func (st *buildStatus) build() error {
13961410
st.buildRecord().put()
1411+
1412+
sp := st.createSpan("checking_for_snapshot")
1413+
if inStaging {
1414+
err := storageClient.Bucket(buildEnv.SnapBucket).Object(st.snapshotObjectName()).Delete(context.Background())
1415+
st.logEventTime("deleted_snapshot", fmt.Sprint(err))
1416+
}
1417+
snapshotExists := st.useSnapshot()
1418+
if inStaging {
1419+
st.logEventTime("use_snapshot", fmt.Sprint(snapshotExists))
1420+
}
1421+
sp.done(nil)
1422+
1423+
if !snapshotExists && st.shouldCrossCompileMake() {
1424+
if err := st.crossCompileMakeAndSnapshot(); err != nil {
1425+
return err
1426+
}
1427+
st.forceSnapshotUsage()
1428+
}
1429+
1430+
sp = st.createSpan("get_buildlet")
13971431
pool := st.buildletPool()
1398-
sp := st.createSpan("get_buildlet")
13991432
bc, err := pool.GetBuildlet(st.ctx, st.conf.HostType, st)
14001433
sp.done(err)
14011434
if err != nil {
@@ -1542,7 +1575,7 @@ func (st *buildStatus) runAllSharded() (remoteErr, err error) {
15421575
return nil, nil
15431576
}
15441577

1545-
if err := st.doSnapshot(); err != nil {
1578+
if err := st.doSnapshot(st.bc); err != nil {
15461579
return nil, err
15471580
}
15481581

@@ -1560,6 +1593,68 @@ func (st *buildStatus) runAllSharded() (remoteErr, err error) {
15601593
return nil, nil
15611594
}
15621595

1596+
func (st *buildStatus) crossCompileMakeAndSnapshot() (err error) {
1597+
// TODO: currently we ditch this buildlet when we're done with
1598+
// the make.bash & snapshot. For extra speed later, we could
1599+
// keep it around and use it to "go test -c" each stdlib
1600+
// package's tests, and push the binary to each ARM helper
1601+
// machine. That might be too little gain for the complexity,
1602+
// though, or slower once we ship everything around.
1603+
ctx, cancel := context.WithCancel(st.ctx)
1604+
defer cancel()
1605+
sp := st.createSpan("get_buildlet_cross")
1606+
kubeBC, err := kubePool.GetBuildlet(ctx, "host-linux-armhf-cross", st)
1607+
sp.done(err)
1608+
if err != nil {
1609+
return err
1610+
}
1611+
defer kubeBC.Close()
1612+
1613+
if err := st.writeGoSourceTo(kubeBC); err != nil {
1614+
return err
1615+
}
1616+
1617+
makeSpan := st.createSpan("make_cross_compile_kube")
1618+
defer func() { makeSpan.done(err) }()
1619+
1620+
goos, goarch := st.conf.GOOS(), st.conf.GOARCH()
1621+
1622+
remoteErr, err := kubeBC.Exec("/bin/bash", buildlet.ExecOpts{
1623+
SystemLevel: true,
1624+
Args: []string{
1625+
"-c",
1626+
"cd $WORKDIR/go/src && " +
1627+
"./make.bash && " +
1628+
"cd .. && " +
1629+
"mv bin/*_*/* bin && " +
1630+
"rmdir bin/*_* && " +
1631+
"rm -rf pkg/linux_amd64 pkg/tool/linux_amd64 pkg/bootstrap pkg/obj",
1632+
},
1633+
Output: st,
1634+
ExtraEnv: []string{
1635+
"GOROOT_BOOTSTRAP=/go1.4",
1636+
"CGO_ENABLED=1",
1637+
"CC_FOR_TARGET=arm-linux-gnueabihf-gcc",
1638+
"GOOS=" + goos,
1639+
"GOARCH=" + goarch,
1640+
"GOARM=7", // harmless if GOARCH != "arm"
1641+
},
1642+
Debug: true,
1643+
})
1644+
if err != nil {
1645+
return err
1646+
}
1647+
if remoteErr != nil {
1648+
return fmt.Errorf("remote error: %v", remoteErr)
1649+
}
1650+
1651+
if err := st.doSnapshot(kubeBC); err != nil {
1652+
return err
1653+
}
1654+
1655+
return nil
1656+
}
1657+
15631658
// runMake builds the tool chain.
15641659
// remoteErr and err are as described at the top of this file.
15651660
func (st *buildStatus) runMake() (remoteErr, err error) {
@@ -1635,16 +1730,16 @@ func (st *buildStatus) runAllLegacy() (remoteErr, err error) {
16351730
return nil, nil
16361731
}
16371732

1638-
func (st *buildStatus) doSnapshot() error {
1733+
func (st *buildStatus) doSnapshot(bc *buildlet.Client) error {
16391734
// If we're using a pre-built snapshot, don't make another.
16401735
if st.useSnapshot() {
16411736
return nil
16421737
}
16431738

1644-
if err := st.cleanForSnapshot(); err != nil {
1739+
if err := st.cleanForSnapshot(bc); err != nil {
16451740
return fmt.Errorf("cleanForSnapshot: %v", err)
16461741
}
1647-
if err := st.writeSnapshot(); err != nil {
1742+
if err := st.writeSnapshot(bc); err != nil {
16481743
return fmt.Errorf("writeSnapshot: %v", err)
16491744
}
16501745
return nil
@@ -1655,7 +1750,7 @@ var timeSnapshotCorruptionFixed = time.Date(2015, time.November, 1, 0, 0, 0, 0,
16551750
// TODO(adg): prune this map over time (might never be necessary, though)
16561751
var snapshotExistsCache = struct {
16571752
sync.Mutex
1658-
m map[builderRev]bool
1753+
m map[builderRev]bool // set; only true values
16591754
}{m: map[builderRev]bool{}}
16601755

16611756
// snapshotExists reports whether the snapshot exists and isn't corrupt.
@@ -1729,18 +1824,22 @@ func (br *builderRev) snapshotExists() (ok bool) {
17291824
}
17301825

17311826
func (st *buildStatus) writeGoSource() error {
1827+
return st.writeGoSourceTo(st.bc)
1828+
}
1829+
1830+
func (st *buildStatus) writeGoSourceTo(bc *buildlet.Client) error {
17321831
// Write the VERSION file.
17331832
sp := st.createSpan("write_version_tar")
1734-
if err := st.bc.PutTar(versionTgz(st.rev), "go"); err != nil {
1833+
if err := bc.PutTar(versionTgz(st.rev), "go"); err != nil {
17351834
return sp.done(fmt.Errorf("writing VERSION tgz: %v", err))
17361835
}
17371836

1738-
srcTar, err := getSourceTgz(st, "go", st.rev, st.trySet != nil)
1837+
srcTar, err := getSourceTgz(st, "go", st.rev, st.isTry())
17391838
if err != nil {
17401839
return err
17411840
}
17421841
sp = st.createSpan("write_go_src_tar")
1743-
if err := st.bc.PutTar(srcTar, "go"); err != nil {
1842+
if err := bc.PutTar(srcTar, "go"); err != nil {
17441843
return sp.done(fmt.Errorf("writing tarball from Gerrit: %v", err))
17451844
}
17461845
return sp.done(nil)
@@ -1756,14 +1855,12 @@ func (st *buildStatus) writeBootstrapToolchain() error {
17561855
return sp.done(st.bc.PutTarFromURL(u, bootstrapDir))
17571856
}
17581857

1759-
var cleanForSnapshotFiles = []string{
1760-
"go/doc/gopher",
1761-
"go/pkg/bootstrap",
1762-
}
1763-
1764-
func (st *buildStatus) cleanForSnapshot() error {
1858+
func (st *buildStatus) cleanForSnapshot(bc *buildlet.Client) error {
17651859
sp := st.createSpan("clean_for_snapshot")
1766-
return sp.done(st.bc.RemoveAll(cleanForSnapshotFiles...))
1860+
return sp.done(bc.RemoveAll(
1861+
"go/doc/gopher",
1862+
"go/pkg/bootstrap",
1863+
))
17671864
}
17681865

17691866
// snapshotObjectName is the cloud storage object name of the
@@ -1778,12 +1875,12 @@ func (br *builderRev) snapshotURL() string {
17781875
return buildEnv.SnapshotURL(br.name, br.rev)
17791876
}
17801877

1781-
func (st *buildStatus) writeSnapshot() (err error) {
1878+
func (st *buildStatus) writeSnapshot(bc *buildlet.Client) (err error) {
17821879
sp := st.createSpan("write_snapshot_to_gcs")
17831880
defer func() { sp.done(err) }()
17841881

17851882
tsp := st.createSpan("fetch_snapshot_reader_from_buildlet")
1786-
tgz, err := st.bc.GetTar("go")
1883+
tgz, err := bc.GetTar("go")
17871884
tsp.done(err)
17881885
if err != nil {
17891886
return err
@@ -1847,6 +1944,11 @@ func (st *buildStatus) distTestList() (names []string, remoteErr, err error) {
18471944
// only do this for slow builders running redundant tests. (That is,
18481945
// tests which have identical behavior across different ports)
18491946
func (st *buildStatus) shouldSkipTest(testName string) bool {
1947+
if inStaging && st.name == "linux-arm" && false {
1948+
if strings.HasPrefix(testName, "go_test:") && testName < "go_test:runtime" {
1949+
return true
1950+
}
1951+
}
18501952
switch testName {
18511953
case "api":
18521954
return st.isTry() && st.name != "linux-amd64"
@@ -2714,6 +2816,7 @@ type span struct {
27142816
event string // event name like "get_foo" or "write_bar"
27152817
optText string // optional details for event
27162818
start time.Time
2819+
end time.Time
27172820
el eventTimeLogger // where we log to at the end; TODO: this will change
27182821
}
27192822

@@ -2735,8 +2838,16 @@ func createSpan(el eventTimeLogger, event string, optText ...string) *span {
27352838
}
27362839
}
27372840

2841+
// done ends a span.
2842+
// It is legal to call done multiple times. Only the first call
2843+
// logs.
2844+
// done always returns its input argument.
27382845
func (s *span) done(err error) error {
2846+
if !s.end.IsZero() {
2847+
return err
2848+
}
27392849
t1 := time.Now()
2850+
s.end = t1
27402851
td := t1.Sub(s.start)
27412852
var text bytes.Buffer
27422853
fmt.Fprintf(&text, "after %v", td)
@@ -2923,6 +3034,10 @@ var sourceGroup singleflight.Group
29233034
var sourceCache = lru.New(40) // git rev -> []byte
29243035

29253036
func useWatcher() bool {
3037+
if inStaging {
3038+
// Adjust as needed, depending on what you're testing.
3039+
return false
3040+
}
29263041
return *mode != "dev"
29273042
}
29283043

dashboard/builders.go

+7
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ var Hosts = map[string]*HostConfig{
2828
buildletURLTmpl: "http://storage.googleapis.com/$BUCKET/buildlet.linux-amd64",
2929
env: []string{"GOROOT_BOOTSTRAP=/go1.4"},
3030
},
31+
"host-linux-armhf-cross": &HostConfig{
32+
Notes: "Kubernetes container on GKE built from env/crosscompile/linux-armhf-jessie",
33+
KubeImage: "linux-armhf-jessie:latest",
34+
buildletURLTmpl: "http://storage.googleapis.com/$BUCKET/buildlet.linux-amd64",
35+
env: []string{"GOROOT_BOOTSTRAP=/go1.4"},
36+
},
37+
3138
"host-nacl-kube": &HostConfig{
3239
Notes: "Kubernetes container on GKE.",
3340
KubeImage: "linux-x86-nacl:latest",

0 commit comments

Comments
 (0)