github.com/sealerio/sealer@v0.11.1-0.20240507115618-f4f89c5853ae/pkg/imageengine/buildah/manifest.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 buildah 16 17 import ( 18 "context" 19 "fmt" 20 "os" 21 "strings" 22 23 "github.com/containers/storage" 24 "github.com/pkg/errors" 25 26 "github.com/containers/buildah/util" 27 "github.com/containers/common/libimage" 28 "github.com/containers/common/libimage/manifests" 29 cp "github.com/containers/image/v5/copy" 30 "github.com/containers/image/v5/manifest" 31 "github.com/containers/image/v5/transports" 32 "github.com/containers/image/v5/transports/alltransports" 33 "github.com/containers/image/v5/types" 34 "github.com/hashicorp/go-multierror" 35 "github.com/opencontainers/go-digest" 36 imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" 37 "github.com/sealerio/sealer/pkg/define/options" 38 "github.com/sirupsen/logrus" 39 ) 40 41 func (engine *Engine) LookupManifest(name string) (*libimage.ManifestList, error) { 42 return engine.libimageRuntime.LookupManifestList(name) 43 } 44 45 func (engine *Engine) CreateManifest(name string, opts *options.ManifestCreateOpts) (string, error) { 46 store := engine.ImageStore() 47 systemCxt := engine.SystemContext() 48 list := manifests.Create() 49 50 names, err := util.ExpandNames([]string{name}, systemCxt, store) 51 if err != nil { 52 return "", fmt.Errorf("encountered while expanding image name %q: %w", name, err) 53 } 54 55 return list.SaveToImage(store, "", names, manifest.DockerV2ListMediaType) 56 } 57 58 func (engine *Engine) DeleteManifests(names []string, opts *options.ManifestDeleteOpts) error { 59 runtime := engine.ImageRuntime() 60 61 rmiReports, rmiErrors := runtime.RemoveImages(context.Background(), names, &libimage.RemoveImagesOptions{ 62 Filters: []string{"readonly=false"}, 63 LookupManifest: true, 64 }) 65 for _, r := range rmiReports { 66 for _, u := range r.Untagged { 67 logrus.Infof("untagged: %s", u) 68 } 69 } 70 for _, r := range rmiReports { 71 if r.Removed { 72 logrus.Infof("%s", r.ID) 73 } 74 } 75 76 var multiE *multierror.Error 77 multiE = multierror.Append(multiE, rmiErrors...) 78 return multiE.ErrorOrNil() 79 } 80 81 func (engine *Engine) InspectManifest(name string, opts *options.ManifestInspectOpts) (*libimage.ManifestListData, error) { 82 runtime := engine.ImageRuntime() 83 84 // attempt to resolve the manifest list locally. 85 manifestList, err := runtime.LookupManifestList(name) 86 if err != nil { 87 return nil, err 88 } 89 90 return manifestList.Inspect() 91 } 92 93 func (engine *Engine) PushManifest(name, destSpec string, opts *options.PushOptions) error { 94 runtime := engine.ImageRuntime() 95 store := engine.ImageStore() 96 systemCxt := engine.SystemContext() 97 systemCxt.OCIInsecureSkipTLSVerify = opts.SkipTLSVerify 98 systemCxt.DockerInsecureSkipTLSVerify = types.NewOptionalBool(opts.SkipTLSVerify) 99 100 manifestList, err := runtime.LookupManifestList(name) 101 if err != nil { 102 return err 103 } 104 105 _, list, err := manifests.LoadFromImage(store, manifestList.ID()) 106 if err != nil { 107 return err 108 } 109 110 dest, err := alltransports.ParseImageName(destSpec) 111 if err != nil { 112 destTransport := strings.Split(destSpec, ":")[0] 113 if t := transports.Get(destTransport); t != nil { 114 return err 115 } 116 117 if strings.Contains(destSpec, "://") { 118 return err 119 } 120 121 destSpec = "docker://" + destSpec 122 dest2, err2 := alltransports.ParseImageName(destSpec) 123 if err2 != nil { 124 return err 125 } 126 dest = dest2 127 logrus.Debugf("Assuming docker:// as the transport method for DESTINATION: %s", destSpec) 128 } 129 130 var manifestType string 131 if opts.Format != "" { 132 switch opts.Format { 133 case "oci": 134 manifestType = imgspecv1.MediaTypeImageManifest 135 case "v2s2", "docker": 136 manifestType = manifest.DockerV2Schema2MediaType 137 default: 138 return fmt.Errorf("unknown format %q. Choose on of the supported formats: 'oci' or 'v2s2'", opts.Format) 139 } 140 } 141 pushOptions := manifests.PushOptions{ 142 Store: store, 143 SystemContext: systemCxt, 144 ImageListSelection: cp.CopySystemImage, 145 Instances: nil, 146 ManifestType: manifestType, 147 } 148 if opts.All { 149 pushOptions.ImageListSelection = cp.CopyAllImages 150 } 151 if !opts.Quiet { 152 pushOptions.ReportWriter = os.Stderr 153 } 154 155 _, _, err = list.Push(getContext(), dest, pushOptions) 156 157 if err == nil && opts.Rm { 158 _, err = store.DeleteImage(manifestList.ID(), true) 159 } 160 161 return err 162 } 163 164 // AddToManifest : 165 // for `manifestName`: if it is not exist,will create a new one. if not, it must be an existed manifest name. 166 // for `imageNameOrIDList`: 167 // if element is a single image just add it, 168 // if element is a manifest will add it’s s all instance no matter what platform it is. 169 func (engine *Engine) AddToManifest(manifestName string, imageNameOrIDList []string, opts *options.ManifestAddOpts) error { 170 var ( 171 runtime = engine.ImageRuntime() 172 ) 173 174 // check whether manifestName is already existed. 175 manifestList, err := runtime.LookupManifestList(manifestName) 176 if err == nil { 177 return engine.addToManifestList(manifestList, imageNameOrIDList, opts) 178 } 179 180 if !errors.Is(err, storage.ErrImageUnknown) { 181 return err 182 } 183 184 logrus.Infof("will create a new one manifest with name %s", manifestName) 185 // if not exit,create a new one 186 _, err = engine.CreateManifest(manifestName, &options.ManifestCreateOpts{}) 187 if err != nil { 188 return fmt.Errorf("failed to create a new one manifest with name %s :%v", manifestName, err) 189 } 190 manifestList, err = runtime.LookupManifestList(manifestName) 191 if err != nil { 192 return err 193 } 194 195 err = engine.addToManifestList(manifestList, imageNameOrIDList, opts) 196 if err != nil { 197 delErr := engine.DeleteManifests([]string{manifestName}, &options.ManifestDeleteOpts{}) 198 if delErr != nil { 199 return fmt.Errorf("failed to delete %s : %v", manifestName, delErr) 200 } 201 return err 202 } 203 204 return nil 205 } 206 207 func (engine *Engine) addToManifestList(manifestList *libimage.ManifestList, imageNameOrIDList []string, opts *options.ManifestAddOpts) error { 208 var ( 209 imageIDToAdd []string 210 err error 211 store = engine.ImageStore() 212 ) 213 214 // determine all images 215 for _, imageNameOrID := range imageNameOrIDList { 216 ret, err := engine.getImageIDList(imageNameOrID) 217 if err != nil { 218 return fmt.Errorf("failed to look up %s", imageNameOrID) 219 } 220 221 imageIDToAdd = append(imageIDToAdd, ret...) 222 } 223 224 _, list, err := manifests.LoadFromImage(store, manifestList.ID()) 225 if err != nil { 226 return err 227 } 228 229 // add each to manifest list 230 for _, imageID := range imageIDToAdd { 231 err = engine.addOneToManifestList(list, imageID, opts) 232 if err != nil { 233 return fmt.Errorf("failed to add new image %s to manifest :%v ", imageID, err) 234 } 235 } 236 237 _, err = list.SaveToImage(store, manifestList.ID(), nil, "") 238 239 return err 240 } 241 242 func (engine *Engine) addOneToManifestList(list manifests.List, imageSpec string, opts *options.ManifestAddOpts) error { 243 store := engine.ImageStore() 244 systemCxt := engine.SystemContext() 245 246 ref, err := alltransports.ParseImageName(imageSpec) 247 if err != nil { 248 if ref, err = alltransports.ParseImageName(util.DefaultTransport + imageSpec); err != nil { 249 // check if the local image exists 250 if ref, _, err = util.FindImage(store, "", systemCxt, imageSpec); err != nil { 251 return err 252 } 253 } 254 } 255 256 digestID, err := list.Add(getContext(), systemCxt, ref, opts.All) 257 if err != nil { 258 var storeErr error 259 // Retry without a custom system context. A user may want to add 260 // a custom platform (see #3511). 261 if ref, _, storeErr = util.FindImage(store, "", nil, imageSpec); storeErr != nil { 262 logrus.Errorf("Error while trying to find image on local storage: %v", storeErr) 263 return err 264 } 265 digestID, storeErr = list.Add(getContext(), systemCxt, ref, opts.All) 266 if storeErr != nil { 267 logrus.Errorf("Error while trying to add on manifest list: %v", storeErr) 268 return err 269 } 270 } 271 272 if opts.Os != "" { 273 if err = list.SetOS(digestID, opts.Os); err != nil { 274 return err 275 } 276 } 277 if opts.OsVersion != "" { 278 if err = list.SetOSVersion(digestID, opts.OsVersion); err != nil { 279 return err 280 } 281 } 282 if len(opts.OsFeatures) != 0 { 283 if err = list.SetOSFeatures(digestID, opts.OsFeatures); err != nil { 284 return err 285 } 286 } 287 if opts.Arch != "" { 288 if err = list.SetArchitecture(digestID, opts.Arch); err != nil { 289 return err 290 } 291 } 292 if opts.Variant != "" { 293 if err = list.SetVariant(digestID, opts.Variant); err != nil { 294 return err 295 } 296 } 297 298 if len(opts.Annotations) != 0 { 299 annotations := make(map[string]string) 300 for _, annotationSpec := range opts.Annotations { 301 spec := strings.SplitN(annotationSpec, "=", 2) 302 if len(spec) != 2 { 303 return fmt.Errorf("no value given for annotation %q", spec[0]) 304 } 305 annotations[spec[0]] = spec[1] 306 } 307 if err = list.SetAnnotations(&digestID, annotations); err != nil { 308 return err 309 } 310 } 311 312 logrus.Infof("adding image %s successfully", imageSpec) 313 314 return nil 315 } 316 317 // getImageId get imageID by name Or id,what ever it is an image or a manifest 318 // if it is image just return imageID 319 // if it is a manifest, return its included instance IDs. 320 func (engine *Engine) getImageIDList(imageNameOrID string) ([]string, error) { 321 // try to look up `imageNameOrID` as ManifestList 322 store := engine.ImageStore() 323 img, _, err := engine.ImageRuntime().LookupImage(imageNameOrID, &libimage.LookupImageOptions{ 324 ManifestList: true, 325 }) 326 if err != nil { 327 return nil, err 328 } 329 330 isManifest, err := img.IsManifestList(getContext()) 331 if err != nil { 332 return nil, err 333 } 334 335 // if not manifest, just return its ID. 336 if !isManifest { 337 return []string{img.ID()}, nil 338 } 339 340 // if it is a manifest, return its included instance ID. 341 logrus.Infof("image %q is a manifest list, looking up matching instances", imageNameOrID) 342 343 imageName := img.Names()[0] 344 manifestList, err := engine.ImageRuntime().LookupManifestList(imageName) 345 if err != nil { 346 return nil, err 347 } 348 349 _, list, err := manifests.LoadFromImage(store, manifestList.ID()) 350 if err != nil { 351 return nil, err 352 } 353 354 var imageIDList []string 355 for _, instanceDigest := range list.Instances() { 356 images, err := store.ImagesByDigest(instanceDigest) 357 if err != nil { 358 return nil, err 359 } 360 if len(images) == 0 { 361 return nil, fmt.Errorf("no image matched with digest %s", instanceDigest) 362 } 363 imageIDList = append(imageIDList, images[0].ID) 364 } 365 366 return imageIDList, nil 367 } 368 369 func (engine *Engine) RemoveFromManifest(name string, instanceDigest digest.Digest, opts *options.ManifestRemoveOpts) error { 370 runtime := engine.ImageRuntime() 371 372 manifestList, err := runtime.LookupManifestList(name) 373 if err != nil { 374 return err 375 } 376 377 if err = manifestList.RemoveInstance(instanceDigest); err != nil { 378 return err 379 } 380 381 logrus.Infof("%s: %s", manifestList.ID(), instanceDigest.String()) 382 383 return nil 384 }