github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/pkg/buildpack/managed_collection.go (about)

     1  package buildpack
     2  
     3  // ManagedCollection keeps track of build modules and the manner in which they should be added to an OCI image (as flattened or exploded).
     4  type ManagedCollection interface {
     5  	// AllModules returns all build modules handled by the manager.
     6  	AllModules() []BuildModule
     7  
     8  	// ExplodedModules returns all build modules that will be added to the output artifact as a single layer
     9  	// containing a single module.
    10  	ExplodedModules() []BuildModule
    11  
    12  	// AddModules adds module information to the collection as flattened or not, depending on how the collection is configured.
    13  	AddModules(main BuildModule, deps ...BuildModule)
    14  
    15  	// FlattenedModules returns all build modules that will be added to the output artifact as a single layer
    16  	// containing multiple modules.
    17  	FlattenedModules() [][]BuildModule
    18  
    19  	// ShouldFlatten returns true if the given module should be flattened.
    20  	ShouldFlatten(module BuildModule) bool
    21  }
    22  
    23  type managedCollection struct {
    24  	explodedModules  []BuildModule
    25  	flattenedModules [][]BuildModule
    26  }
    27  
    28  func (f *managedCollection) ExplodedModules() []BuildModule {
    29  	return f.explodedModules
    30  }
    31  
    32  func (f *managedCollection) FlattenedModules() [][]BuildModule {
    33  	return f.flattenedModules
    34  }
    35  
    36  func (f *managedCollection) AllModules() []BuildModule {
    37  	all := f.explodedModules
    38  	for _, modules := range f.flattenedModules {
    39  		all = append(all, modules...)
    40  	}
    41  	return all
    42  }
    43  
    44  func (f *managedCollection) ShouldFlatten(module BuildModule) bool {
    45  	for _, modules := range f.flattenedModules {
    46  		for _, v := range modules {
    47  			if v == module {
    48  				return true
    49  			}
    50  		}
    51  	}
    52  	return false
    53  }
    54  
    55  // managedCollectionV1 can be used to flatten all the flattenModuleInfos or none of them.
    56  type managedCollectionV1 struct {
    57  	managedCollection
    58  	flattenAll bool
    59  }
    60  
    61  // NewManagedCollectionV1 will create a manager instance responsible for flattening Buildpack Packages.
    62  func NewManagedCollectionV1(flattenAll bool) ManagedCollection {
    63  	return &managedCollectionV1{
    64  		flattenAll: flattenAll,
    65  		managedCollection: managedCollection{
    66  			explodedModules:  []BuildModule{},
    67  			flattenedModules: [][]BuildModule{},
    68  		},
    69  	}
    70  }
    71  
    72  func (f *managedCollectionV1) AddModules(main BuildModule, deps ...BuildModule) {
    73  	if !f.flattenAll {
    74  		// default behavior
    75  		f.explodedModules = append(f.explodedModules, append([]BuildModule{main}, deps...)...)
    76  	} else {
    77  		// flatten all
    78  		if len(f.flattenedModules) == 1 {
    79  			// we already have data in the array, append to the first element
    80  			f.flattenedModules[0] = append(f.flattenedModules[0], append([]BuildModule{main}, deps...)...)
    81  		} else {
    82  			// the array is empty, create the first element
    83  			f.flattenedModules = append(f.flattenedModules, append([]BuildModule{main}, deps...))
    84  		}
    85  	}
    86  }
    87  
    88  // NewManagedCollectionV2 will create a manager instance responsible for flattening buildpacks inside a Builder.
    89  // The flattened build modules provided are the groups of buildpacks that must be put together in a single layer; the manager
    90  // will take care of keeping them in the correct group (flattened or exploded) once they are added.
    91  func NewManagedCollectionV2(modules FlattenModuleInfos) ManagedCollection {
    92  	flattenGroups := 0
    93  	if modules != nil {
    94  		flattenGroups = len(modules.FlattenModules())
    95  	}
    96  
    97  	return &managedCollectionV2{
    98  		flattenModuleInfos: modules,
    99  		managedCollection: managedCollection{
   100  			explodedModules:  []BuildModule{},
   101  			flattenedModules: make([][]BuildModule, flattenGroups),
   102  		},
   103  	}
   104  }
   105  
   106  // managedCollectionV2 can be used when the build modules to be flattened are known at the point of initialization.
   107  // The flattened build modules are provided when the collection is initialized and the collection will take care of
   108  // keeping them in the correct group (flattened or exploded) once they are added.
   109  type managedCollectionV2 struct {
   110  	managedCollection
   111  	flattenModuleInfos FlattenModuleInfos
   112  }
   113  
   114  func (ff *managedCollectionV2) flattenGroups() []ModuleInfos {
   115  	return ff.flattenModuleInfos.FlattenModules()
   116  }
   117  
   118  func (ff *managedCollectionV2) AddModules(main BuildModule, deps ...BuildModule) {
   119  	var allModules []BuildModule
   120  	allModules = append(allModules, append([]BuildModule{main}, deps...)...)
   121  	for _, module := range allModules {
   122  		if ff.flattenModuleInfos != nil && len(ff.flattenGroups()) > 0 {
   123  			pos := ff.flattenedLayerFor(module)
   124  			if pos >= 0 {
   125  				ff.flattenedModules[pos] = append(ff.flattenedModules[pos], module)
   126  			} else {
   127  				// this module must not be flattened
   128  				ff.explodedModules = append(ff.explodedModules, module)
   129  			}
   130  		} else {
   131  			// we don't want to flatten anything
   132  			ff.explodedModules = append(ff.explodedModules, module)
   133  		}
   134  	}
   135  }
   136  
   137  // flattenedLayerFor given a module will try to determine which row (layer) this module must be added to in order to be flattened.
   138  // If the layer is not found, it means the module must not be flattened at all.
   139  func (ff *managedCollectionV2) flattenedLayerFor(module BuildModule) int {
   140  	// flattenGroups is a two-dimensional array, where each row represents
   141  	// a group of module infos that must be flattened together in the same layer.
   142  	for i, flattenGroup := range ff.flattenGroups() {
   143  		for _, buildModuleInfo := range flattenGroup.BuildModule() {
   144  			if buildModuleInfo.FullName() == module.Descriptor().Info().FullName() {
   145  				return i
   146  			}
   147  		}
   148  	}
   149  	return -1
   150  }