163 lines
5.0 KiB
Go
163 lines
5.0 KiB
Go
package launch
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/buildpacks/lifecycle/api"
|
|
)
|
|
|
|
// ProcessFor creates a process from container cmd
|
|
//
|
|
// If the Platform API if 0.4 or greater and DefaultProcess is set:
|
|
// * The default process is returned with `cmd` appended to the process args
|
|
// If the Platform API is less than 0.4
|
|
// * If there is exactly one argument and it matches a process type, it returns that process.
|
|
// * If cmd is empty, it returns the default process
|
|
// Else
|
|
// * it constructs a new process from cmd
|
|
// * If the first element in cmd is `cmd` the process shall be direct
|
|
func (l *Launcher) ProcessFor(cmd []string) (Process, error) {
|
|
if l.DefaultProcessType == "" {
|
|
process, err := l.userProvidedProcess(cmd)
|
|
if err != nil {
|
|
return Process{}, err
|
|
}
|
|
return process, nil
|
|
}
|
|
|
|
process, ok := l.findProcessType(l.DefaultProcessType)
|
|
if !ok {
|
|
return Process{}, fmt.Errorf("process type %s was not found", l.DefaultProcessType)
|
|
}
|
|
|
|
if l.PlatformAPI.LessThan("0.10") {
|
|
return l.handleUserArgsPlatformLessThan010(process, cmd)
|
|
}
|
|
return l.handleUserArgs(process, cmd)
|
|
}
|
|
|
|
func (l *Launcher) handleUserArgsPlatformLessThan010(process Process, userArgs []string) (Process, error) {
|
|
process.Args = append(process.Args, userArgs...)
|
|
return process, nil
|
|
}
|
|
|
|
func (l *Launcher) handleUserArgs(process Process, userArgs []string) (Process, error) {
|
|
switch {
|
|
case len(process.Command.Entries) > 1: // definitely newer buildpack
|
|
overridableArgs := process.Args
|
|
process.Args = process.Command.Entries[1:] // set always-provided args
|
|
process.Command.Entries = []string{process.Command.Entries[0]} // when exec'ing the process we always expect Command to have just one entry
|
|
if len(userArgs) > 0 {
|
|
process.Args = append(process.Args, userArgs...)
|
|
} else {
|
|
process.Args = append(process.Args, overridableArgs...)
|
|
}
|
|
case len(userArgs) == 0:
|
|
// nothing to do, we just provide whatever the original process args were
|
|
default:
|
|
// we have user-provided args, and we need to check the buildpack API to know how to handle them
|
|
bp, err := l.buildpackForProcess(process)
|
|
if err != nil {
|
|
return Process{}, err
|
|
}
|
|
if api.MustParse(bp.API).LessThan("0.9") {
|
|
process.Args = append(process.Args, userArgs...) // user-provided args are appended to process args
|
|
} else {
|
|
process.Args = userArgs // user-provided args replace process args
|
|
}
|
|
}
|
|
|
|
return process, nil
|
|
}
|
|
|
|
func (l *Launcher) buildpackForProcess(process Process) (Buildpack, error) {
|
|
for _, bp := range l.Buildpacks {
|
|
if bp.ID == process.BuildpackID {
|
|
return bp, nil
|
|
}
|
|
}
|
|
return Buildpack{}, fmt.Errorf("failed to find buildpack for process %s with buildpack ID %s", process.Type, process.BuildpackID)
|
|
}
|
|
|
|
func (l *Launcher) processForLegacy(cmd []string) (Process, error) {
|
|
if len(cmd) == 0 {
|
|
if process, ok := l.findProcessType(l.DefaultProcessType); ok {
|
|
return process, nil
|
|
}
|
|
|
|
return Process{}, fmt.Errorf("process type %s was not found", l.DefaultProcessType)
|
|
}
|
|
|
|
if len(cmd) == 1 {
|
|
if process, ok := l.findProcessType(cmd[0]); ok {
|
|
return process, nil
|
|
}
|
|
}
|
|
|
|
return l.userProvidedProcess(cmd)
|
|
}
|
|
|
|
func (l *Launcher) findProcessType(pType string) (Process, bool) {
|
|
for _, p := range l.Processes {
|
|
if p.Type == pType && l.isProcessEligibleForExecEnv(p) {
|
|
return p, true
|
|
}
|
|
}
|
|
|
|
return Process{}, false
|
|
}
|
|
|
|
// isProcessEligibleForExecEnv checks if a process is eligible for the current execution environment
|
|
// According to the spec:
|
|
// - A process is eligible if it has no exec-env specified, OR
|
|
// - Its exec-env includes the current execution environment, OR
|
|
// - Its exec-env includes the special value "*" which indicates compatibility with all environments
|
|
func (l *Launcher) isProcessEligibleForExecEnv(p Process) bool {
|
|
// Note: This is gated by Platform API 0.15, not Buildpack API
|
|
// The platform controls the Platform API version and determines when this is available
|
|
if l.PlatformAPI == nil || !l.PlatformAPI.AtLeast("0.15") {
|
|
return true // Skip execution environment filtering for older Platform APIs
|
|
}
|
|
|
|
// If no exec-env specified, process applies to all execution environments
|
|
if len(p.ExecEnv) == 0 {
|
|
return true
|
|
}
|
|
|
|
// Check if process supports all execution environments
|
|
for _, env := range p.ExecEnv {
|
|
if env == "*" {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Check if process supports the current execution environment
|
|
for _, env := range p.ExecEnv {
|
|
if env == l.ExecEnv {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (l *Launcher) userProvidedProcess(cmd []string) (Process, error) {
|
|
if len(cmd) == 0 {
|
|
return Process{}, errors.New("when there is no default process a command is required")
|
|
}
|
|
if len(cmd) > 1 && cmd[0] == "--" {
|
|
return Process{Command: RawCommand{Entries: []string{cmd[1]}}, Args: cmd[2:], Direct: true}, nil
|
|
}
|
|
|
|
return Process{Command: RawCommand{Entries: []string{cmd[0]}}, Args: cmd[1:]}, nil
|
|
}
|
|
|
|
func getProcessWorkingDirectory(process Process, appDir string) string {
|
|
if process.WorkingDirectory == "" {
|
|
return appDir
|
|
}
|
|
return process.WorkingDirectory
|
|
}
|