354 lines
9.8 KiB
Go
354 lines
9.8 KiB
Go
package platform
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/buildpacks/imgutil/remote"
|
|
"github.com/google/go-containerregistry/pkg/authn"
|
|
|
|
"github.com/buildpacks/lifecycle/api"
|
|
"github.com/buildpacks/lifecycle/internal/str"
|
|
"github.com/buildpacks/lifecycle/log"
|
|
)
|
|
|
|
// LifecycleInputs holds the values of command-line flags and args i.e., platform inputs to the lifecycle.
|
|
// Fields are the cumulative total of inputs across all lifecycle phases and all supported Platform APIs.
|
|
type LifecycleInputs struct {
|
|
PlatformAPI *api.Version
|
|
AnalyzedPath string
|
|
AppDir string
|
|
BuildConfigDir string
|
|
BuildImageRef string
|
|
BuildpacksDir string
|
|
CacheDir string
|
|
CacheImageRef string
|
|
DefaultProcessType string
|
|
DeprecatedRunImageRef string
|
|
ExecEnv string
|
|
ExtendKind string
|
|
ExtendedDir string
|
|
ExtensionsDir string
|
|
GeneratedDir string
|
|
GroupPath string
|
|
KanikoDir string
|
|
LaunchCacheDir string
|
|
LauncherPath string
|
|
LauncherSBOMDir string
|
|
LayersDir string
|
|
LayoutDir string
|
|
LogLevel string
|
|
OrderPath string
|
|
OutputImageRef string
|
|
PlanPath string
|
|
PlatformDir string
|
|
PreviousImageRef string
|
|
ProjectMetadataPath string
|
|
ReportPath string
|
|
RunImageRef string
|
|
RunPath string
|
|
StackPath string
|
|
SystemPath string
|
|
UID int
|
|
GID int
|
|
ForceRebase bool
|
|
NoColor bool
|
|
ParallelExport bool
|
|
SkipLayers bool
|
|
UseDaemon bool
|
|
UseLayout bool
|
|
AdditionalTags str.Slice // str.Slice satisfies the `Value` interface required by the `flag` package
|
|
KanikoCacheTTL time.Duration
|
|
InsecureRegistries str.Slice
|
|
}
|
|
|
|
const PlaceholderLayers = "<layers>"
|
|
|
|
// NewLifecycleInputs constructs new lifecycle inputs for the provided Platform API version.
|
|
// Inputs can be specified by the platform (in order of precedence) through:
|
|
// - command-line flags
|
|
// - environment variables
|
|
// - falling back to the default value
|
|
//
|
|
// NewLifecycleInputs provides, for each input, the value from the environment if specified, falling back to the default.
|
|
// As the final value of the layers directory (if provided via the command-line) is not known,
|
|
// inputs that default to a child of the layers directory are provided with PlaceholderLayers as the layers directory.
|
|
// To be valid, inputs obtained from calling NewLifecycleInputs MUST be updated using UpdatePlaceholderPaths
|
|
// once the final value of the layers directory is known.
|
|
func NewLifecycleInputs(platformAPI *api.Version) *LifecycleInputs {
|
|
// FIXME: api compatibility should be validated here
|
|
|
|
var skipLayers bool
|
|
if boolEnv(EnvSkipLayers) || boolEnv(EnvSkipRestore) {
|
|
skipLayers = true
|
|
}
|
|
|
|
inputs := &LifecycleInputs{
|
|
// Operator config
|
|
|
|
LogLevel: envOrDefault(EnvLogLevel, DefaultLogLevel),
|
|
NoColor: boolEnv(EnvNoColor),
|
|
PlatformAPI: platformAPI,
|
|
ExtendKind: envOrDefault(EnvExtendKind, DefaultExtendKind),
|
|
UseDaemon: boolEnv(EnvUseDaemon),
|
|
InsecureRegistries: sliceEnv(EnvInsecureRegistries),
|
|
UseLayout: boolEnv(EnvUseLayout),
|
|
|
|
// Provided by the base image
|
|
|
|
UID: intEnv(EnvUID),
|
|
GID: intEnv(EnvGID),
|
|
|
|
// Provided by the builder image
|
|
|
|
BuildConfigDir: envOrDefault(EnvBuildConfigDir, DefaultBuildConfigDir),
|
|
BuildpacksDir: envOrDefault(EnvBuildpacksDir, DefaultBuildpacksDir),
|
|
ExtensionsDir: envOrDefault(EnvExtensionsDir, DefaultExtensionsDir),
|
|
RunPath: envOrDefault(EnvRunPath, DefaultRunPath),
|
|
StackPath: envOrDefault(EnvStackPath, DefaultStackPath),
|
|
SystemPath: envOrDefault(EnvSystemPath, CNBSystemPath),
|
|
|
|
// Provided at build time
|
|
|
|
AppDir: envOrDefault(EnvAppDir, DefaultAppDir),
|
|
ExecEnv: envOrDefault(EnvExecEnv, DefaultExecEnv),
|
|
LayersDir: envOrDefault(EnvLayersDir, DefaultLayersDir),
|
|
LayoutDir: os.Getenv(EnvLayoutDir),
|
|
OrderPath: envOrDefault(EnvOrderPath, filepath.Join(PlaceholderLayers, DefaultOrderFile)),
|
|
PlatformDir: envOrDefault(EnvPlatformDir, DefaultPlatformDir),
|
|
|
|
// The following instruct the lifecycle where to write files and data during the build
|
|
|
|
AnalyzedPath: envOrDefault(EnvAnalyzedPath, filepath.Join(PlaceholderLayers, DefaultAnalyzedFile)),
|
|
ExtendedDir: envOrDefault(EnvExtendedDir, filepath.Join(PlaceholderLayers, DefaultExtendedDir)),
|
|
GeneratedDir: envOrDefault(EnvGeneratedDir, filepath.Join(PlaceholderLayers, DefaultGeneratedDir)),
|
|
GroupPath: envOrDefault(EnvGroupPath, filepath.Join(PlaceholderLayers, DefaultGroupFile)),
|
|
PlanPath: envOrDefault(EnvPlanPath, filepath.Join(PlaceholderLayers, DefaultPlanFile)),
|
|
ReportPath: envOrDefault(EnvReportPath, filepath.Join(PlaceholderLayers, DefaultReportFile)),
|
|
|
|
// Configuration options with respect to caching
|
|
|
|
CacheDir: os.Getenv(EnvCacheDir),
|
|
CacheImageRef: os.Getenv(EnvCacheImage),
|
|
KanikoCacheTTL: timeEnvOrDefault(EnvKanikoCacheTTL, DefaultKanikoCacheTTL),
|
|
KanikoDir: "/kaniko",
|
|
LaunchCacheDir: os.Getenv(EnvLaunchCacheDir),
|
|
SkipLayers: skipLayers,
|
|
ParallelExport: boolEnv(EnvParallelExport),
|
|
|
|
// Images used by the lifecycle during the build
|
|
|
|
AdditionalTags: nil, // no default
|
|
BuildImageRef: os.Getenv(EnvBuildImage),
|
|
DeprecatedRunImageRef: "", // no default
|
|
OutputImageRef: "", // no default
|
|
PreviousImageRef: os.Getenv(EnvPreviousImage),
|
|
RunImageRef: os.Getenv(EnvRunImage),
|
|
|
|
// Configuration options for the output application image
|
|
|
|
DefaultProcessType: os.Getenv(EnvProcessType),
|
|
LauncherPath: DefaultLauncherPath,
|
|
LauncherSBOMDir: DefaultBuildpacksioSBOMDir,
|
|
ProjectMetadataPath: envOrDefault(EnvProjectMetadataPath, filepath.Join(PlaceholderLayers, DefaultProjectMetadataFile)),
|
|
|
|
// Configuration options for rebasing
|
|
ForceRebase: boolEnv(EnvForceRebase),
|
|
}
|
|
|
|
return inputs
|
|
}
|
|
|
|
func (i *LifecycleInputs) AccessChecker() CheckReadAccess {
|
|
if i.UseDaemon || i.UseLayout {
|
|
// nop checker
|
|
return func(_ string, _ authn.Keychain) (bool, error) {
|
|
return true, nil
|
|
}
|
|
}
|
|
// remote access checker
|
|
return func(repo string, keychain authn.Keychain) (bool, error) {
|
|
img, err := remote.NewImage(repo, keychain)
|
|
if err != nil {
|
|
return false, fmt.Errorf("failed to get remote image: %w", err)
|
|
}
|
|
return img.CheckReadAccess()
|
|
}
|
|
}
|
|
|
|
type CheckReadAccess func(repo string, keychain authn.Keychain) (bool, error)
|
|
|
|
func (i *LifecycleInputs) DestinationImages() []string {
|
|
var ret []string
|
|
ret = appendOnce(ret, i.OutputImageRef)
|
|
ret = appendOnce(ret, i.AdditionalTags...)
|
|
return ret
|
|
}
|
|
|
|
func (i *LifecycleInputs) Images() []string {
|
|
var ret []string
|
|
ret = appendOnce(ret, i.DestinationImages()...)
|
|
ret = appendOnce(ret, i.PreviousImageRef, i.BuildImageRef, i.RunImageRef, i.DeprecatedRunImageRef, i.CacheImageRef)
|
|
return ret
|
|
}
|
|
|
|
func (i *LifecycleInputs) RegistryImages() []string {
|
|
var ret []string
|
|
ret = appendOnce(ret, i.CacheImageRef)
|
|
if i.UseDaemon {
|
|
return ret
|
|
}
|
|
ret = appendOnce(ret, i.Images()...)
|
|
return ret
|
|
}
|
|
|
|
func appendOnce(list []string, els ...string) []string {
|
|
for _, el := range els {
|
|
if el == "" {
|
|
continue
|
|
}
|
|
if notIn(list, el) {
|
|
list = append(list, el)
|
|
}
|
|
}
|
|
return list
|
|
}
|
|
|
|
func notIn(list []string, str string) bool {
|
|
for _, el := range list {
|
|
if el == str {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// shared helpers
|
|
|
|
func boolEnv(k string) bool {
|
|
v := os.Getenv(k)
|
|
b, err := strconv.ParseBool(v)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return b
|
|
}
|
|
|
|
func envOrDefault(key string, defaultVal string) string {
|
|
if envVal := os.Getenv(key); envVal != "" {
|
|
return envVal
|
|
}
|
|
return defaultVal
|
|
}
|
|
|
|
func sliceEnv(k string) str.Slice {
|
|
envVal := os.Getenv(k)
|
|
if envVal != "" {
|
|
return strings.Split(envVal, ",")
|
|
}
|
|
return str.Slice(nil)
|
|
}
|
|
|
|
func intEnv(k string) int {
|
|
v := os.Getenv(k)
|
|
d, err := strconv.Atoi(v)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
return d
|
|
}
|
|
|
|
func timeEnvOrDefault(key string, defaultVal time.Duration) time.Duration {
|
|
envTTL := os.Getenv(key)
|
|
if envTTL == "" {
|
|
return defaultVal
|
|
}
|
|
ttl, err := time.ParseDuration(envTTL)
|
|
if err != nil {
|
|
return defaultVal
|
|
}
|
|
return ttl
|
|
}
|
|
|
|
// operations
|
|
|
|
func UpdatePlaceholderPaths(i *LifecycleInputs, _ log.Logger) error {
|
|
toUpdate := i.placeholderPaths()
|
|
for _, path := range toUpdate {
|
|
if *path == "" {
|
|
continue
|
|
}
|
|
if !isPlaceholder(*path) {
|
|
continue
|
|
}
|
|
oldPath := *path
|
|
toReplace := PlaceholderLayers
|
|
if i.LayersDir == "" { // layers is unset when this call comes from the rebaser
|
|
toReplace = PlaceholderLayers + string(filepath.Separator)
|
|
}
|
|
newPath := strings.Replace(*path, toReplace, i.LayersDir, 1)
|
|
*path = newPath
|
|
if isPlaceholderOrder(oldPath) {
|
|
if _, err := os.Stat(newPath); err != nil {
|
|
i.OrderPath = CNBOrderPath
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func isPlaceholder(s string) bool {
|
|
return strings.Contains(s, PlaceholderLayers)
|
|
}
|
|
|
|
func isPlaceholderOrder(s string) bool {
|
|
return s == filepath.Join(PlaceholderLayers, DefaultOrderFile)
|
|
}
|
|
|
|
func (i *LifecycleInputs) placeholderPaths() []*string {
|
|
return []*string{
|
|
&i.AnalyzedPath,
|
|
&i.ExtendedDir,
|
|
&i.GeneratedDir,
|
|
&i.GroupPath,
|
|
&i.OrderPath,
|
|
&i.PlanPath,
|
|
&i.ProjectMetadataPath,
|
|
&i.ReportPath,
|
|
}
|
|
}
|
|
|
|
func ResolveAbsoluteDirPaths(i *LifecycleInputs, _ log.Logger) error {
|
|
toUpdate := i.directoryPaths()
|
|
for _, dir := range toUpdate {
|
|
if *dir == "" {
|
|
continue
|
|
}
|
|
abs, err := filepath.Abs(*dir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*dir = abs
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (i *LifecycleInputs) directoryPaths() []*string {
|
|
return []*string{
|
|
&i.AppDir,
|
|
&i.BuildConfigDir,
|
|
&i.BuildpacksDir,
|
|
&i.CacheDir,
|
|
&i.ExtensionsDir,
|
|
&i.GeneratedDir,
|
|
&i.KanikoDir,
|
|
&i.LaunchCacheDir,
|
|
&i.LayersDir,
|
|
&i.PlatformDir,
|
|
}
|
|
}
|