Skip to content

Commit

Permalink
Refactor lifecycle phase factories
Browse files Browse the repository at this point in the history
Signed-off-by: Natalie Arellano <[email protected]>
  • Loading branch information
natalieparellano committed Sep 26, 2023
1 parent 6c7d16a commit 7ec8967
Show file tree
Hide file tree
Showing 16 changed files with 476 additions and 447 deletions.
14 changes: 4 additions & 10 deletions cmd/lifecycle/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/buildpacks/lifecycle/cmd"
"github.com/buildpacks/lifecycle/cmd/lifecycle/cli"
"github.com/buildpacks/lifecycle/image"
"github.com/buildpacks/lifecycle/internal/encoding"
"github.com/buildpacks/lifecycle/phase"
"github.com/buildpacks/lifecycle/platform"
"github.com/buildpacks/lifecycle/priv"
Expand Down Expand Up @@ -96,26 +95,21 @@ func (a *analyzeCmd) Privileges() error {

// Exec executes the command.
func (a *analyzeCmd) Exec() error {
factory := phase.NewAnalyzerFactory(
factory := phase.NewConnectedFactory(
a.PlatformAPI,
&cmd.BuildpackAPIVerifier{},
NewCacheHandler(a.keychain),
phase.NewConfigHandler(),
phase.Config,
image.NewHandler(a.docker, a.keychain, a.LayoutDir, a.UseLayout, a.InsecureRegistries),
image.NewRegistryHandler(a.keychain, a.InsecureRegistries),
)
analyzer, err := factory.NewAnalyzer(a.AdditionalTags, a.CacheImageRef, a.LaunchCacheDir, a.LayersDir, a.OutputImageRef, a.PreviousImageRef, a.RunImageRef, a.SkipLayers, cmd.DefaultLogger)
analyzer, err := factory.NewAnalyzer(*a.LifecycleInputs, cmd.DefaultLogger)
if err != nil {
return unwrapErrorFailWithMessage(err, "initialize analyzer")
}
analyzedMD, err := analyzer.Analyze()
if err != nil {
return cmd.FailErrCode(err, a.CodeFor(platform.AnalyzeError), "analyze")
}
cmd.DefaultLogger.Debugf("Run image info in analyzed metadata is: ")
cmd.DefaultLogger.Debugf(encoding.ToJSONMaybe(analyzedMD.RunImage))
if err = encoding.WriteTOML(a.AnalyzedPath, analyzedMD); err != nil {
return cmd.FailErr(err, "write analyzed")
}
return nil
return phase.Config.WriteAnalyzed(a.AnalyzedPath, &analyzedMD, cmd.DefaultLogger)
}
14 changes: 8 additions & 6 deletions cmd/lifecycle/creator.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,32 +123,35 @@ func (c *createCmd) Exec() error {
plan files.Plan
)
cmd.DefaultLogger.Phase("ANALYZING")
analyzerFactory := phase.NewAnalyzerFactory(
analyzerFactory := phase.NewConnectedFactory(
c.PlatformAPI,
&cmd.BuildpackAPIVerifier{},
NewCacheHandler(c.keychain),
phase.NewConfigHandler(),
image.NewHandler(c.docker, c.keychain, c.LayoutDir, c.UseLayout, c.InsecureRegistries),
image.NewRegistryHandler(c.keychain, c.InsecureRegistries),
)
analyzer, err := analyzerFactory.NewAnalyzer(c.AdditionalTags, c.CacheImageRef, c.LaunchCacheDir, c.LayersDir, c.OutputImageRef, c.PreviousImageRef, c.RunImageRef, c.SkipLayers, cmd.DefaultLogger)
analyzer, err := analyzerFactory.NewAnalyzer(*c.LifecycleInputs, cmd.DefaultLogger)
if err != nil {
return unwrapErrorFailWithMessage(err, "initialize analyzer")
}
analyzedMD, err = analyzer.Analyze()
if err != nil {
return err
}
if err := phase.Config.WriteAnalyzed(c.AnalyzedPath, &analyzedMD, cmd.DefaultLogger); err != nil {
return err
}

// Detect
cmd.DefaultLogger.Phase("DETECTING")
detectorFactory := phase.NewDetectorFactory(
detectorFactory := phase.NewHermeticFactory(
c.PlatformAPI,
&cmd.BuildpackAPIVerifier{},
phase.NewConfigHandler(),
dirStore,
)
detector, err := detectorFactory.NewDetector(analyzedMD, c.AppDir, c.BuildConfigDir, c.OrderPath, c.PlatformDir, cmd.DefaultLogger)
detector, err := detectorFactory.NewDetector(*c.LifecycleInputs, cmd.DefaultLogger)
if err != nil {
return unwrapErrorFailWithMessage(err, "initialize detector")
}
Expand All @@ -164,8 +167,7 @@ func (c *createCmd) Exec() error {
Platform: c.Platform,
keychain: c.keychain,
}
err := restoreCmd.restore(analyzedMD.LayersMetadata, group, cacheStore)
if err != nil {
if err := restoreCmd.restore(analyzedMD.LayersMetadata, group, cacheStore); err != nil {
return err
}
}
Expand Down
62 changes: 15 additions & 47 deletions cmd/lifecycle/detector.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"github.com/buildpacks/lifecycle/buildpack"
"github.com/buildpacks/lifecycle/cmd"
"github.com/buildpacks/lifecycle/cmd/lifecycle/cli"
"github.com/buildpacks/lifecycle/internal/encoding"
"github.com/buildpacks/lifecycle/phase"
"github.com/buildpacks/lifecycle/platform"
"github.com/buildpacks/lifecycle/platform/files"
Expand Down Expand Up @@ -60,22 +59,14 @@ func (d *detectCmd) Privileges() error {

func (d *detectCmd) Exec() error {
dirStore := platform.NewDirStore(d.BuildpacksDir, d.ExtensionsDir)
detectorFactory := phase.NewDetectorFactory(
detectorFactory := phase.NewHermeticFactory(
d.PlatformAPI,
&cmd.BuildpackAPIVerifier{},
phase.NewConfigHandler(),
dirStore,
)
amd, err := files.ReadAnalyzed(d.AnalyzedPath, cmd.DefaultLogger)
if err != nil {
return unwrapErrorFailWithMessage(err, "reading analyzed.toml")
}
detector, err := detectorFactory.NewDetector(
amd,
d.AppDir,
d.BuildConfigDir,
d.OrderPath,
d.PlatformDir,
*d.LifecycleInputs,
cmd.DefaultLogger,
)
if err != nil {
Expand All @@ -86,27 +77,20 @@ func (d *detectCmd) Exec() error {
return err
}
}
group, plan, err := doDetect(detector, d.Platform)
group, _, err := doDetect(detector, d.Platform)
if err != nil {
return err // pass through error
}
if group.HasExtensions() {
generatorFactory := phase.NewGeneratorFactory(
generatorFactory := phase.NewHermeticFactory(
d.PlatformAPI,
&cmd.BuildpackAPIVerifier{},
phase.Config,
dirStore,
)
var generator *phase.Generator
generator, err = generatorFactory.NewGenerator(
d.AnalyzedPath,
d.AppDir,
d.BuildConfigDir,
group.GroupExtensions,
d.GeneratedDir,
plan,
d.PlatformAPI,
d.PlatformDir,
d.RunPath,
*d.LifecycleInputs,
cmd.Stdout, cmd.Stderr,
cmd.DefaultLogger,
)
Expand All @@ -118,16 +102,14 @@ func (d *detectCmd) Exec() error {
if err != nil {
return d.unwrapGenerateFail(err)
}

if err = d.writeGenerateData(result.AnalyzedMD); err != nil {
if err := phase.Config.WriteAnalyzed(d.AnalyzedPath, &result.AnalyzedMD, cmd.DefaultLogger); err != nil {
return err
}
// was the build plan updated?
if result.UsePlan {
plan = result.Plan
if err := phase.Config.WritePlan(d.PlanPath, &result.Plan); err != nil {
return err
}
}
return d.writeDetectData(group, plan)
return nil
}

func unwrapErrorFailWithMessage(err error, msg string) error {
Expand Down Expand Up @@ -167,25 +149,11 @@ func doDetect(detector *phase.Detector, p *platform.Platform) (buildpack.Group,
return buildpack.Group{}, files.Plan{}, cmd.FailErrCode(err, p.CodeFor(platform.DetectError), "detect")
}
}
return group, plan, nil
}

func (d *detectCmd) writeDetectData(group buildpack.Group, plan files.Plan) error {
if err := encoding.WriteTOML(d.GroupPath, group); err != nil {
return cmd.FailErr(err, "write buildpack group")
}
if err := encoding.WriteTOML(d.PlanPath, plan); err != nil {
return cmd.FailErr(err, "write detect plan")
if err := phase.Config.WriteGroup(p.GroupPath, &group); err != nil {
return buildpack.Group{}, files.Plan{}, err
}
return nil
}

// writeGenerateData re-outputs the analyzedMD that we read previously, but now we've added the RunImage, if a custom runImage was configured
func (d *detectCmd) writeGenerateData(analyzedMD files.Analyzed) error {
cmd.DefaultLogger.Debugf("Run image info in analyzed metadata is: ")
cmd.DefaultLogger.Debugf(encoding.ToJSONMaybe(analyzedMD.RunImage))
if err := encoding.WriteTOML(d.AnalyzedPath, analyzedMD); err != nil {
return cmd.FailErr(err, "write analyzed metadata")
if err := phase.Config.WritePlan(p.PlanPath, &plan); err != nil {
return buildpack.Group{}, files.Plan{}, err
}
return nil
return group, plan, nil
}
12 changes: 2 additions & 10 deletions cmd/lifecycle/extender.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,14 @@ func (e *extendCmd) Privileges() error {
}

func (e *extendCmd) Exec() error {
extenderFactory := phase.NewExtenderFactory(&cmd.BuildpackAPIVerifier{}, phase.NewConfigHandler())
extenderFactory := phase.NewHermeticFactory(e.PlatformAPI, &cmd.BuildpackAPIVerifier{}, phase.NewConfigHandler(), platform.NewDirStore("", ""))
applier, err := kaniko.NewDockerfileApplier()
if err != nil {
return err
}
extender, err := extenderFactory.NewExtender(
e.AnalyzedPath,
e.AppDir,
e.ExtendedDir,
e.GeneratedDir,
e.GroupPath,
e.LayersDir,
e.PlatformDir,
e.KanikoCacheTTL,
*e.LifecycleInputs,
applier,
e.ExtendKind,
cmd.DefaultLogger,
)
if err != nil {
Expand Down
102 changes: 10 additions & 92 deletions phase/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"github.com/pkg/errors"

"github.com/buildpacks/lifecycle/api"
"github.com/buildpacks/lifecycle/cache"
"github.com/buildpacks/lifecycle/image"
"github.com/buildpacks/lifecycle/internal/fsutil"
"github.com/buildpacks/lifecycle/internal/layer"
Expand All @@ -14,33 +13,8 @@ import (
"github.com/buildpacks/lifecycle/platform/files"
)

type AnalyzerFactory struct {
platformAPI *api.Version
apiVerifier BuildpackAPIVerifier
cacheHandler CacheHandler
configHandler ConfigHandler
imageHandler image.Handler
registryHandler image.RegistryHandler
}

func NewAnalyzerFactory(
platformAPI *api.Version,
apiVerifier BuildpackAPIVerifier,
cacheHandler CacheHandler,
configHandler ConfigHandler,
imageHandler image.Handler,
registryHandler image.RegistryHandler,
) *AnalyzerFactory {
return &AnalyzerFactory{
platformAPI: platformAPI,
apiVerifier: apiVerifier,
cacheHandler: cacheHandler,
configHandler: configHandler,
imageHandler: imageHandler,
registryHandler: registryHandler,
}
}

// Analyzer reads metadata from the previous image (if it exists) and the run image,
// and additionally restores the SBOM layer from the previous image for use later in the build.
type Analyzer struct {
PreviousImage imgutil.Image
RunImage imgutil.Image
Expand All @@ -50,90 +24,34 @@ type Analyzer struct {
}

// NewAnalyzer configures a new Analyzer according to the provided Platform API version.
func (f *AnalyzerFactory) NewAnalyzer(additionalTags []string, cacheImageRef string, launchCacheDir string, layersDir string, outputImageRef string, previousImageRef string, runImageRef string, skipLayers bool, logger log.Logger) (*Analyzer, error) {
func (f *ConnectedFactory) NewAnalyzer(inputs platform.LifecycleInputs, logger log.Logger) (*Analyzer, error) {
analyzer := &Analyzer{
Logger: logger,
SBOMRestorer: &layer.NopSBOMRestorer{},
PlatformAPI: f.platformAPI,
}

if err := f.ensureRegistryAccess(additionalTags, cacheImageRef, outputImageRef, runImageRef, previousImageRef); err != nil {
if err := f.ensureRegistryAccess(inputs); err != nil {
return nil, err
}

if f.platformAPI.AtLeast("0.8") && !skipLayers {
analyzer.SBOMRestorer = &layer.DefaultSBOMRestorer{ // FIXME: eventually layer.NewSBOMRestorer should always return the default one, and then we can use the constructor
LayersDir: layersDir,
if f.platformAPI.AtLeast("0.8") && !inputs.SkipLayers {
analyzer.SBOMRestorer = &layer.DefaultSBOMRestorer{
LayersDir: inputs.LayersDir,
Logger: logger,
}
}

if err := f.setPrevious(analyzer, previousImageRef, launchCacheDir); err != nil {
var err error
if analyzer.PreviousImage, err = f.getPreviousImage(inputs.PreviousImageRef, inputs.LaunchCacheDir); err != nil {
return nil, err
}
if err := f.setRun(analyzer, runImageRef); err != nil {
if analyzer.RunImage, err = f.getRunImage(inputs.RunImageRef); err != nil {
return nil, err
}
return analyzer, nil
}

func (f *AnalyzerFactory) ensureRegistryAccess(
additionalTags []string,
cacheImageRef string,
outputImageRef string,
runImageRef string,
previousImageRef string,
) error {
var readImages, writeImages []string
writeImages = append(writeImages, cacheImageRef)
if f.imageHandler.Kind() == image.RemoteKind {
readImages = append(readImages, previousImageRef, runImageRef)
writeImages = append(writeImages, outputImageRef)
writeImages = append(writeImages, additionalTags...)
}

if err := f.registryHandler.EnsureReadAccess(readImages...); err != nil {
return errors.Wrap(err, "validating registry read access")
}
if err := f.registryHandler.EnsureWriteAccess(writeImages...); err != nil {
return errors.Wrap(err, "validating registry write access")
}
return nil
}

func (f *AnalyzerFactory) setPrevious(analyzer *Analyzer, imageRef string, launchCacheDir string) error {
if imageRef == "" {
return nil
}
var err error
analyzer.PreviousImage, err = f.imageHandler.InitImage(imageRef)
if err != nil {
return errors.Wrap(err, "getting previous image")
}
if launchCacheDir == "" || f.imageHandler.Kind() != image.LocalKind {
return nil
}

volumeCache, err := cache.NewVolumeCache(launchCacheDir)
if err != nil {
return errors.Wrap(err, "creating launch cache")
}
analyzer.PreviousImage = cache.NewCachingImage(analyzer.PreviousImage, volumeCache)
return nil
}

func (f *AnalyzerFactory) setRun(analyzer *Analyzer, imageRef string) error {
if imageRef == "" {
return nil
}
var err error
analyzer.RunImage, err = f.imageHandler.InitImage(imageRef)
if err != nil {
return errors.Wrap(err, "getting run image")
}
return nil
}

// Analyze fetches the layers metadata from the previous image and writes analyzed.toml.
func (a *Analyzer) Analyze() (files.Analyzed, error) {
defer log.NewMeasurement("Analyzer", a.Logger)()
Expand Down
Loading

0 comments on commit 7ec8967

Please sign in to comment.