cmd/config/entrypoint/main.go (106 lines of code) (raw):

// 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. // Implements config/entrypoint buildpack. // The entrypoint buildpack sets the image entrypoint based on an environment variable or Procfile. package main import ( "fmt" "os" "regexp" "strings" "github.com/GoogleCloudPlatform/buildpacks/pkg/appengine" "github.com/GoogleCloudPlatform/buildpacks/pkg/appyaml" "github.com/GoogleCloudPlatform/buildpacks/pkg/env" gcp "github.com/GoogleCloudPlatform/buildpacks/pkg/gcpbuildpack" ) var ( processRe = regexp.MustCompile(`(?m)^(\w+):\s*(.+)$`) ) func main() { gcp.Main(detectFn, buildFn) } func detectFn(ctx *gcp.Context) (gcp.DetectResult, error) { // Detection for GAE and GCF if env.IsGAE() || env.IsGCF() { return gcp.OptInEnvSet(env.XGoogleTargetPlatform), nil } // Detection for GCP builds follows if os.Getenv(env.Entrypoint) != "" { return gcp.OptInEnvSet(env.Entrypoint), nil } procExists, err := ctx.FileExists("Procfile") if err != nil { return nil, err } if procExists { return gcp.OptInFileFound("Procfile"), nil } if entrypoint, _ := appyaml.EntrypointIfExists(ctx.ApplicationRoot()); entrypoint != "" { ctx.Logf("Using entrypoint from app.yaml.") return gcp.OptIn("Found the app.yaml file specified by GAE_APPLICATION_YAML_PATH."), nil } return gcp.OptOut(fmt.Sprintf( "%s not set, no valid entrypoint in app.yaml and Procfile not found", env.Entrypoint)), nil } func buildFn(ctx *gcp.Context) error { if env.IsGCF() { // Function Frameworks with the function target will automatically build correctly without entrypoint modification. return nil } if env.IsGAE() { runtime, ok := os.LookupEnv(env.Runtime) if !ok { return gcp.InternalErrorf("env.%s required for GAE platform.", env.XGoogleTargetPlatform) } return appengine.Build(ctx, runtime, nil) } if entrypoint := os.Getenv(env.Entrypoint); entrypoint != "" { ctx.AddProcess(gcp.WebProcess, []string{entrypoint}, gcp.AsDefaultProcess()) ctx.Logf("Using entrypoint from environment variable %s: %s", env.Entrypoint, entrypoint) return nil } procExists, err := ctx.FileExists("Procfile") if err != nil { return err } if procExists { b, err := ctx.ReadFile("Procfile") if err != nil { return err } return addProcfileProcesses(ctx, string(b)) } entrypoint, err := appyaml.EntrypointIfExists(ctx.ApplicationRoot()) if err != nil { return gcp.UserErrorf(fmt.Sprintf( "app.yaml env var set but the specified app.yaml file doesn't exist.")) } if entrypoint != "" { ctx.AddProcess(gcp.WebProcess, []string{entrypoint}, gcp.AsDefaultProcess()) ctx.Logf("Using entrypoint from app.yaml.") return nil } return gcp.UserErrorf(fmt.Sprintf( "%s not set, no valid entrypoint config in Procfile or app.yaml.", env.Entrypoint)) } // addProcfileProcesses adds all processes from the given Procfile contents. func addProcfileProcesses(ctx *gcp.Context, content string) error { matches := processRe.FindAllStringSubmatch(content, -1) if len(matches) == 0 { return gcp.UserErrorf("did not find any processes in Procfile") } found := make(map[string]bool, len(matches)) for _, match := range matches { // Sanity check, if this fails there is a mistake in the regex. // One group for overall match and two subgroups. if len(match) != 3 { return gcp.InternalErrorf("invalid process match, want slice of two strings, got: %v", match) } name, command := match[1], strings.TrimSpace(match[2]) if found[name] { ctx.Warnf("Skipping duplicate %s process: %s", gcp.WebProcess, command) continue } found[name] = true if name == gcp.WebProcess { ctx.Logf("Using entrypoint from Procfile: %s", command) ctx.AddProcess(name, []string{command}, gcp.AsDefaultProcess()) } else { ctx.AddProcess(name, []string{command}) } } if !found[gcp.WebProcess] { return gcp.UserErrorf("web process not found in Procfile: %#v", matches) } return nil }