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 }