github.com/alibaba/sealer@v0.8.6-0.20220430115802-37a2bdaa8173/pkg/image/merge.go (about) 1 // Copyright © 2021 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 image 16 17 import ( 18 "context" 19 "fmt" 20 21 "github.com/alibaba/sealer/pkg/image/reference" 22 23 "github.com/alibaba/sealer/utils" 24 25 "github.com/alibaba/sealer/common" 26 "github.com/opencontainers/go-digest" 27 "golang.org/x/sync/errgroup" 28 "sigs.k8s.io/yaml" 29 30 "github.com/alibaba/sealer/pkg/image/store" 31 v1 "github.com/alibaba/sealer/types/api/v1" 32 ) 33 34 func save(imageName string, image *v1.Image) error { 35 var ( 36 imageBytes []byte 37 imageStore store.ImageStore 38 err error 39 ) 40 named, err := reference.ParseToNamed(imageName) 41 if err != nil { 42 return err 43 } 44 image.Name = named.CompleteName() 45 imageStore, err = store.NewDefaultImageStore() 46 if err != nil { 47 return err 48 } 49 50 imageBytes, err = yaml.Marshal(image) 51 if err != nil { 52 return err 53 } 54 imageID := digest.FromBytes(imageBytes).Hex() 55 image.Spec.ID = imageID 56 err = setClusterFile(named.CompleteName(), image) 57 if err != nil { 58 return err 59 } 60 return imageStore.Save(*image) 61 } 62 63 func Merge(imageName string, images []string, platform *v1.Platform) error { 64 if imageName == "" { 65 return fmt.Errorf("target image name should not be nil") 66 } 67 var ( 68 err error 69 newIma *v1.Image 70 imageStore store.ImageStore 71 ) 72 imageStore, err = store.NewDefaultImageStore() 73 if err != nil { 74 return err 75 } 76 77 d := DefaultImageService{imageStore: imageStore} 78 eg, _ := errgroup.WithContext(context.Background()) 79 80 for _, ima := range images { 81 im := ima 82 plats := []*v1.Platform{platform} 83 eg.Go(func() error { 84 err = d.PullIfNotExist(im, plats) 85 if err != nil { 86 return err 87 } 88 return nil 89 }) 90 } 91 if err := eg.Wait(); err != nil { 92 return err 93 } 94 95 for i, v := range images { 96 img, err := d.GetImageByName(v, platform) 97 if err != nil { 98 return err 99 } 100 if i == 0 { 101 newIma = img 102 continue 103 } else { 104 newIma, err = merge(newIma, img) 105 if err != nil { 106 return err 107 } 108 } 109 } 110 return save(imageName, newIma) 111 } 112 113 func merge(base, ima *v1.Image) (*v1.Image, error) { 114 if base == nil || ima == nil { 115 return nil, fmt.Errorf(" merge base or new can not be nil") 116 } 117 // merge image platform 118 if base.Spec.Platform.OS != ima.Spec.Platform.OS || 119 base.Spec.Platform.Architecture != ima.Spec.Platform.Architecture || 120 base.Spec.Platform.Variant != ima.Spec.Platform.Variant { 121 return nil, fmt.Errorf("can not merge different platform") 122 } 123 124 var ( 125 isApp = base.Spec.ImageConfig.ImageType == common.AppImage && 126 ima.Spec.ImageConfig.ImageType == common.AppImage 127 ) 128 // merge image type;only if two image is application image we can determine this new image is application image. 129 if isApp { 130 base.Spec.ImageConfig.ImageType = common.AppImage 131 } 132 133 // merge image config arg and remove duplicate value 134 base.Spec.ImageConfig.Args = mergeImageArg(base.Spec.ImageConfig.Args, ima.Spec.ImageConfig.Args, isApp) 135 // merge image config cmd and remove duplicate value 136 base.Spec.ImageConfig.Cmd = mergeImageCmd(base.Spec.ImageConfig.Cmd, ima.Spec.ImageConfig.Cmd, isApp) 137 138 // merge image layer 139 res := append(base.Spec.Layers, ima.Spec.Layers...) 140 base.Spec.Layers = removeDuplicateLayers(res) 141 return base, nil 142 } 143 144 func mergeImageCmd(base, ima v1.ImageCmd, isApp bool) v1.ImageCmd { 145 current := utils.MergeSlice(base.Current, ima.Current) 146 if isApp { 147 return v1.ImageCmd{ 148 Current: current, 149 } 150 } 151 return v1.ImageCmd{ 152 Current: current, 153 Parent: utils.MergeSlice(base.Parent, ima.Parent), 154 } 155 } 156 157 func mergeImageArg(base, ima v1.ImageArg, isApp bool) v1.ImageArg { 158 for k, v := range ima.Current { 159 base.Current[k] = v 160 } 161 162 if isApp { 163 return v1.ImageArg{ 164 Current: base.Current, 165 } 166 } 167 168 for k, v := range ima.Parent { 169 base.Parent[k] = v 170 } 171 172 return v1.ImageArg{ 173 Parent: base.Parent, 174 Current: base.Current, 175 } 176 } 177 178 func removeDuplicateLayers(list []v1.Layer) []v1.Layer { 179 var result []v1.Layer 180 flagMap := map[string]struct{}{} 181 for _, v := range list { 182 // if id is not nil,remove duplicate id,this covers run and copy instruction. 183 if v.ID.String() != "" { 184 if _, ok := flagMap[v.ID.String()]; !ok { 185 flagMap[v.ID.String()] = struct{}{} 186 result = append(result, v) 187 } 188 } 189 } 190 return result 191 }