github.com/alibaba/sealer@v0.8.6-0.20220430115802-37a2bdaa8173/build/buildimage/executor.go (about) 1 // Copyright © 2022 Alibaba Group Holding Ltd. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package buildimage 16 17 import ( 18 "context" 19 "errors" 20 "fmt" 21 22 "github.com/alibaba/sealer/utils/mount" 23 24 "github.com/alibaba/sealer/build/buildinstruction" 25 "github.com/alibaba/sealer/common" 26 "github.com/alibaba/sealer/logger" 27 "github.com/alibaba/sealer/pkg/image" 28 "github.com/alibaba/sealer/pkg/image/store" 29 v1 "github.com/alibaba/sealer/types/api/v1" 30 "github.com/alibaba/sealer/utils" 31 "golang.org/x/sync/errgroup" 32 ) 33 34 const ( 35 maxLayerDeep = 128 36 ) 37 38 type layerExecutor struct { 39 platform v1.Platform 40 baseLayers []v1.Layer 41 layerStore store.LayerStore 42 rootfsMountInfo mount.Service 43 } 44 45 func (l *layerExecutor) Execute(ctx Context, rawLayers []v1.Layer) ([]v1.Layer, error) { 46 var ( 47 execCtx buildinstruction.ExecContext 48 baseLayers = l.baseLayers 49 ) 50 51 // process middleware file 52 err := l.checkMiddleware(ctx.BuildContext) 53 if err != nil { 54 return []v1.Layer{}, err 55 } 56 57 execCtx = buildinstruction.NewExecContext(ctx.BuildContext, ctx.BuildArgs, 58 ctx.UseCache, l.layerStore) 59 60 for i := 0; i < len(rawLayers); i++ { 61 //we are to set layer id for each new layers. 62 layer := &rawLayers[i] 63 logger.Info("run build layer: %s %s", layer.Type, layer.Value) 64 65 if layer.Type == common.CMDCOMMAND { 66 continue 67 } 68 69 //run layer instruction exec to get layer id and cache id 70 ic := buildinstruction.InstructionContext{ 71 BaseLayers: baseLayers, 72 CurrentLayer: layer, 73 Platform: l.platform, 74 } 75 inst, err := buildinstruction.NewInstruction(ic) 76 if err != nil { 77 return []v1.Layer{}, err 78 } 79 out, err := inst.Exec(execCtx) 80 if err != nil { 81 return []v1.Layer{}, err 82 } 83 84 // update current layer cache status for next cache 85 if execCtx.ContinueCache { 86 execCtx.ParentID = out.ParentID 87 execCtx.ContinueCache = out.ContinueCache 88 } 89 layer.ID = out.LayerID 90 if out.LayerID == "" { 91 continue 92 } 93 baseLayers = append(baseLayers, *layer) 94 } 95 logger.Info("exec all build instructs success") 96 97 // process differ of manifests and metadata. 98 err = l.checkDiff(rawLayers) 99 if err != nil { 100 return []v1.Layer{}, err 101 } 102 103 upper := l.rootfsMountInfo.GetMountUpper() 104 layer, err := l.genNewLayer(common.BaseImageLayerType, common.RootfsLayerValue, upper) 105 if err != nil { 106 return []v1.Layer{}, err 107 } 108 109 if layer.ID != "" { 110 baseLayers = append(baseLayers, layer) 111 } else { 112 logger.Warn("no rootfs diff content found") 113 } 114 115 return baseLayers, nil 116 } 117 118 func (l *layerExecutor) checkMiddleware(buildContext string) error { 119 var ( 120 rootfs = l.rootfsMountInfo.GetMountTarget() 121 middlewares = []Differ{NewMiddlewarePuller(l.platform)} 122 ) 123 logger.Info("start to check the middleware file") 124 eg, _ := errgroup.WithContext(context.Background()) 125 for _, middleware := range middlewares { 126 s := middleware 127 eg.Go(func() error { 128 err := s.Process(buildContext, rootfs) 129 if err != nil { 130 return err 131 } 132 return nil 133 }) 134 } 135 return eg.Wait() 136 } 137 138 func (l *layerExecutor) checkDiff(rawLayers []v1.Layer) error { 139 var ( 140 rootfs = l.rootfsMountInfo.GetMountTarget() 141 eg, _ = errgroup.WithContext(context.Background()) 142 differs = []Differ{NewRegistryDiffer(l.platform), NewMetadataDiffer()} 143 ) 144 mi, err := GetLayerMountInfo(rawLayers) 145 if err != nil { 146 return err 147 } 148 defer mi.CleanUp() 149 150 srcPath := mi.GetMountTarget() 151 for _, diff := range differs { 152 d := diff 153 eg.Go(func() error { 154 err = d.Process(srcPath, rootfs) 155 if err != nil { 156 return err 157 } 158 return nil 159 }) 160 } 161 return eg.Wait() 162 } 163 164 func (l *layerExecutor) genNewLayer(layerType, layerValue, filepath string) (v1.Layer, error) { 165 imageLayer := v1.Layer{ 166 Type: layerType, 167 Value: layerValue, 168 } 169 170 layerID, err := l.layerStore.RegisterLayerForBuilder(filepath) 171 if err != nil { 172 return imageLayer, fmt.Errorf("failed to register layer, err: %v", err) 173 } 174 175 imageLayer.ID = layerID 176 return imageLayer, nil 177 } 178 179 func (l *layerExecutor) Cleanup() error { 180 l.rootfsMountInfo.CleanUp() 181 return nil 182 } 183 184 func NewLayerExecutor(baseLayers []v1.Layer, platform v1.Platform) (Executor, error) { 185 mountInfo, err := GetLayerMountInfo(baseLayers) 186 if err != nil { 187 return nil, err 188 } 189 layerStore, err := store.NewDefaultLayerStore() 190 if err != nil { 191 return nil, err 192 } 193 194 return &layerExecutor{ 195 baseLayers: baseLayers, 196 layerStore: layerStore, 197 rootfsMountInfo: mountInfo, 198 platform: platform, 199 }, nil 200 } 201 202 // NewBuildImageByKubefile init image spec by kubefile and check if base image exists ,if not will pull it. 203 func NewBuildImageByKubefile(kubefileName string, platform v1.Platform) (*v1.Image, []v1.Layer, error) { 204 rawImage, err := initImageSpec(kubefileName) 205 if err != nil { 206 return nil, nil, err 207 } 208 209 imageStore, err := store.NewDefaultImageStore() 210 if err != nil { 211 return nil, nil, err 212 } 213 214 service, err := image.NewImageService() 215 if err != nil { 216 return nil, nil, err 217 } 218 219 var ( 220 layer0 = rawImage.Spec.Layers[0] 221 baseImage *v1.Image 222 ) 223 224 // and the layer 0 must be from layer 225 if layer0.Value == common.ImageScratch { 226 // give an empty image 227 baseImage = &v1.Image{} 228 } else { 229 plats := []*v1.Platform{&platform} 230 if err = service.PullIfNotExist(layer0.Value, plats); err != nil { 231 return nil, nil, fmt.Errorf("failed to pull baseImage: %v", err) 232 } 233 baseImage, err = imageStore.GetByName(layer0.Value, &platform) 234 if err != nil { 235 return nil, nil, fmt.Errorf("failed to get base image err: %s", err) 236 } 237 } 238 239 baseLayers := append([]v1.Layer{}, baseImage.Spec.Layers...) 240 newLayers := append([]v1.Layer{}, rawImage.Spec.Layers[1:]...) 241 if len(baseLayers)+len(newLayers) > maxLayerDeep { 242 return nil, nil, errors.New("current number of layers exceeds 128 layers") 243 } 244 245 // merge base image cmd and set to raw image as parent. 246 rawImage.Spec.ImageConfig.Cmd.Parent = utils.MergeSlice(baseImage.Spec.ImageConfig.Cmd.Parent, 247 baseImage.Spec.ImageConfig.Cmd.Current) 248 // merge base image args and set to raw image as parent. 249 rawImage.Spec.ImageConfig.Args.Parent = utils.MergeMap(baseImage.Spec.ImageConfig.Args.Parent, 250 baseImage.Spec.ImageConfig.Args.Current) 251 252 return rawImage, baseLayers, nil 253 }