Skip to content

Commit

Permalink
Build application from a srv directory in Go App Engine gomod buildpack
Browse files Browse the repository at this point in the history
This fixes an incompatiblity with the appengine package which expects files to be compiled from srv for go.mod apps: https://github.com/golang/appengine/blob/553959209a20f3be281c16dd5be5c740a893978f/delay/delay.go#L136.

All source code is moved from /workspace to <layer>/srv and `go build` is then invoked with the latter as the work directory. The resulting binary will correctly have /srv/ in the stored file paths.

PiperOrigin-RevId: 327901991
Change-Id: Ic08452e67af3f6c20741e6661931cec717460930
  • Loading branch information
lukasberger authored and copybara-github committed Aug 22, 2020
1 parent 582453f commit 6f91880
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 5 deletions.
5 changes: 4 additions & 1 deletion builders/gae/go111/acceptance/acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ func TestAcceptance(t *testing.T) {
Name: "gopath no dependencies",
App: "gopath",
},

// Test that GOOGLE_BUILDABLE takes precedence over app.yaml and go-app-stager.
{
Name: "gomod GOOGLE_BUILDABLE vs go-app-stager vs app.yaml main package",
Expand Down Expand Up @@ -107,6 +106,10 @@ func TestAcceptance(t *testing.T) {
Name: "gomod no dependencies",
App: "gomod",
},
{
Name: "gomod appengine",
App: "gomod_appengine",
},
}
for _, tc := range testCases {
tc := tc
Expand Down
1 change: 1 addition & 0 deletions builders/testdata/go/gomod_appengine/_main-package-path
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.
5 changes: 5 additions & 0 deletions builders/testdata/go/gomod_appengine/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module example.com/package

go 1.11

require rsc.io/quote v1.5.2
56 changes: 56 additions & 0 deletions builders/testdata/go/gomod_appengine/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package main tests building source that has a go.mod file without gomod dependencies.
// It also tests compatiblity with the golang/appengine package.
package main

import (
"fmt"
"net/http"
"os"
"path/filepath"
"runtime"
"strings"

"rsc.io/quote"
)

func handler(w http.ResponseWriter, r *http.Request) {
if got, want := quote.Hello(), "Hello, world."; got != want {
fmt.Fprintf(w, "FAIL: quote.Hello() = %s, want %s\n", got, want)
return
}

// Check that the path at compile time includes /srv/. This is done for compatibility with the
// appengine package, which requires it for historical reasons. See the go/appengine_gomod
// buildpack.
_, file, _, ok := runtime.Caller(0)
if !ok {
fmt.Fprintln(w, "FAIL: runtime.Caller(0) not ok")
return
}
sep := string(filepath.Separator)
srv := sep + "srv" + sep
if !strings.Contains(file, srv) {
fmt.Fprintf(w, "FAIL: caller file path %s does not contain %s\n", file, srv)
return
}
fmt.Fprintln(w, "PASS")
}

func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":"+os.Getenv("PORT"), nil)
}
1 change: 1 addition & 0 deletions cmd/go/appengine_gomod/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ go_binary(
deps = [
"//pkg/env",
"//pkg/gcpbuildpack",
"//pkg/golang",
],
)

Expand Down
9 changes: 9 additions & 0 deletions cmd/go/appengine_gomod/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (

"github.com/GoogleCloudPlatform/buildpacks/pkg/env"
gcp "github.com/GoogleCloudPlatform/buildpacks/pkg/gcpbuildpack"
"github.com/GoogleCloudPlatform/buildpacks/pkg/golang"
)

const (
Expand Down Expand Up @@ -66,6 +67,14 @@ func buildFn(ctx *gcp.Context) error {
l := ctx.Layer("main_env", gcp.BuildLayer)
l.BuildEnvironment.Override(env.Buildable, buildMainPath)

// HACK: For backwards compatibility on App Engine Go 1.11:
// Copy all files to a layer directory that ends with /srv because the appengine package relies on the name:
// https://github.com/golang/appengine/blob/553959209a20f3be281c16dd5be5c740a893978f/delay/delay.go#L136
// We change the work directory instead of modifying env.Buildable, because the latter is not necessarily a filesystem path.
srvl := ctx.Layer("srv", gcp.BuildLayer)
srvl.BuildEnvironment.Override(golang.BuildDirEnv, srvl.Path)
ctx.Exec([]string{"cp", "--dereference", "-R", ".", srvl.Path}, gcp.WithUserTimingAttribution)

return nil
}

Expand Down
8 changes: 6 additions & 2 deletions cmd/go/appengine_gopath/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,17 @@ func buildFn(ctx *gcp.Context) error {
l.BuildEnvironment.Override(env.Buildable, buildMainPath)
}

// Unlike in the appengine_gomod buildpack, we do not have to compile gopath apps from a path that ends in /srv/. There are two cases:
// * _gopath/main-package-path exists and app source is put on GOPATH, which is handled by:
// https://github.com/golang/appengine/blob/553959209a20f3be281c16dd5be5c740a893978f/delay/delay.go#L136.
// * _gopath/main-package-path does not exist and the app is built from the current directory, which is handled by:
// https://github.com/golang/appengine/blob/553959209a20f3be281c16dd5be5c740a893978f/delay/delay.go#L125-L127

// TODO(b/145608768): Investigate creating and caching a GOCACHE layer.
return nil
}

func copyDir(ctx *gcp.Context, src, dst string) {
ctx.Debugf("copying %q to %q", src, dst)

// Trailing "/." copies the contents of src directory, but not src itself.
src = filepath.Clean(src) + string(filepath.Separator) + "."
ctx.Exec([]string{"cp", "--dereference", "-R", src, dst}, gcp.WithUserTimingAttribution)
Expand Down
9 changes: 7 additions & 2 deletions cmd/go/build/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,14 @@ func buildFn(ctx *gcp.Context) error {
bld = append(bld, goBuildFlags()...)
bld = append(bld, "-o", outBin)
bld = append(bld, buildable)
ctx.Exec(bld, gcp.WithEnv("GOCACHE="+cl.Path), gcp.WithMessageProducer(printTipsAndKeepStderrTail(ctx)), gcp.WithUserAttribution)
// BuildDirEnv should only be set by App Engine buildpacks.
workdir := os.Getenv(golang.BuildDirEnv)
if workdir == "" {
workdir = ctx.ApplicationRoot()
}
ctx.Exec(bld, gcp.WithEnv("GOCACHE="+cl.Path), gcp.WithWorkDir(workdir), gcp.WithMessageProducer(printTipsAndKeepStderrTail(ctx)), gcp.WithUserAttribution)

// Configure the entrypoint for production. Use the full path to save `skaffold debug`
// Configure the entrypoint for production. Use the full path to save `skaffold debug`
// from fetching the remote container image (tens to hundreds of megabytes), which is slow.
if !devmode.Enabled(ctx) {
ctx.AddWebProcess([]string{outBin})
Expand Down
2 changes: 2 additions & 0 deletions pkg/golang/golang.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import (
const (
// OutBin is the name of the final compiled binary produced by Go buildpacks.
OutBin = "main"
// BuildDirEnv is an environment variable that buildpacks can use to communicate the working directory to `go build`.
BuildDirEnv = "GOOGLE_INTERNAL_BUILD_DIR"
)

var (
Expand Down

0 comments on commit 6f91880

Please sign in to comment.