diff --git a/cmd/lifecycle/cli/flags.go b/cmd/lifecycle/cli/flags.go index a469a60bd..d384fd9f6 100644 --- a/cmd/lifecycle/cli/flags.go +++ b/cmd/lifecycle/cli/flags.go @@ -108,6 +108,11 @@ func FlagPlanPath(planPath *string) { flagSet.StringVar(planPath, "plan", *planPath, "path to plan.toml") } +// FlagParallelExport parses `parallel` flag +func FlagParallelExport(parallelExport *bool) { + flagSet.BoolVar(parallelExport, "parallel", *parallelExport, "export app image and cache image in parallel") +} + func FlagPlatformDir(platformDir *string) { flagSet.StringVar(platformDir, "platform", *platformDir, "path to platform directory") } diff --git a/cmd/lifecycle/creator.go b/cmd/lifecycle/creator.go index 708525131..57fadff4d 100644 --- a/cmd/lifecycle/creator.go +++ b/cmd/lifecycle/creator.go @@ -48,6 +48,7 @@ func (c *createCmd) DefineFlags() { cli.FlagLauncherPath(&c.LauncherPath) cli.FlagLayersDir(&c.LayersDir) cli.FlagOrderPath(&c.OrderPath) + cli.FlagParallelExport(&c.ParallelExport) cli.FlagPlatformDir(&c.PlatformDir) cli.FlagPreviousImage(&c.PreviousImageRef) cli.FlagProcessType(&c.DefaultProcessType) @@ -75,6 +76,11 @@ func (c *createCmd) Args(nargs int, args []string) error { return err } } + if c.ParallelExport { + if c.CacheImageRef == "" { + cmd.DefaultLogger.Warn("parallel export has been enabled, but it has not taken effect because cache image (-cache-image) has not been specified.") + } + } return nil } diff --git a/cmd/lifecycle/exporter.go b/cmd/lifecycle/exporter.go index 847be1b46..131ecec8d 100644 --- a/cmd/lifecycle/exporter.go +++ b/cmd/lifecycle/exporter.go @@ -70,6 +70,7 @@ func (e *exportCmd) DefineFlags() { cli.FlagLaunchCacheDir(&e.LaunchCacheDir) cli.FlagLauncherPath(&e.LauncherPath) cli.FlagLayersDir(&e.LayersDir) + cli.FlagParallelExport(&e.ParallelExport) cli.FlagProcessType(&e.DefaultProcessType) cli.FlagProjectMetadataPath(&e.ProjectMetadataPath) cli.FlagReportPath(&e.ReportPath) @@ -229,6 +230,11 @@ func (e *exportCmd) export(group buildpack.Group, cacheStore lifecycle.Cache, an }) }() + // waiting here if parallel export is not enabled + if !e.ParallelExport { + exportWaitGroup.Wait() + } + exportWaitGroup.Add(1) go func() { defer exportWaitGroup.Done() diff --git a/platform/defaults.go b/platform/defaults.go index cfd1d60c9..603a7b191 100644 --- a/platform/defaults.go +++ b/platform/defaults.go @@ -186,6 +186,9 @@ const ( // EnvKanikoCacheTTL is the amount of time to persist layers cached by kaniko during the `extend` phase. EnvKanikoCacheTTL = "CNB_KANIKO_CACHE_TTL" + + // EnvParallelExport is a flag used to instruct the lifecycle to export of application image and cache image in parallel, if true. + EnvParallelExport = "CNB_PARALLEL_EXPORT" ) // DefaultKanikoCacheTTL is the default kaniko cache TTL (2 weeks). diff --git a/platform/lifecycle_inputs.go b/platform/lifecycle_inputs.go index e7cbde28e..dae5234d8 100644 --- a/platform/lifecycle_inputs.go +++ b/platform/lifecycle_inputs.go @@ -54,6 +54,7 @@ type LifecycleInputs struct { UID int GID int ForceRebase bool + ParallelExport bool SkipLayers bool UseDaemon bool UseLayout bool @@ -129,6 +130,7 @@ func NewLifecycleInputs(platformAPI *api.Version) *LifecycleInputs { KanikoDir: "/kaniko", LaunchCacheDir: os.Getenv(EnvLaunchCacheDir), SkipLayers: skipLayers, + ParallelExport: boolEnv(EnvParallelExport), // Images used by the lifecycle during the build @@ -147,6 +149,7 @@ func NewLifecycleInputs(platformAPI *api.Version) *LifecycleInputs { ProjectMetadataPath: envOrDefault(EnvProjectMetadataPath, filepath.Join(PlaceholderLayers, DefaultProjectMetadataFile)), // Configuration options for rebasing + ForceRebase: boolEnv(EnvForceRebase), }