github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/libpod/image/image.go (about) 1 package image 2 3 import ( 4 "context" 5 "encoding/json" 6 stderrors "errors" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "os" 11 "path/filepath" 12 "sort" 13 "strings" 14 "syscall" 15 "time" 16 17 cp "github.com/containers/image/v5/copy" 18 "github.com/containers/image/v5/directory" 19 dockerarchive "github.com/containers/image/v5/docker/archive" 20 "github.com/containers/image/v5/docker/reference" 21 "github.com/containers/image/v5/image" 22 "github.com/containers/image/v5/manifest" 23 ociarchive "github.com/containers/image/v5/oci/archive" 24 is "github.com/containers/image/v5/storage" 25 "github.com/containers/image/v5/tarball" 26 "github.com/containers/image/v5/transports" 27 "github.com/containers/image/v5/transports/alltransports" 28 "github.com/containers/image/v5/types" 29 "github.com/containers/libpod/libpod/driver" 30 "github.com/containers/libpod/libpod/events" 31 "github.com/containers/libpod/pkg/inspect" 32 "github.com/containers/libpod/pkg/registries" 33 "github.com/containers/libpod/pkg/util" 34 "github.com/containers/storage" 35 "github.com/opencontainers/go-digest" 36 imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" 37 ociv1 "github.com/opencontainers/image-spec/specs-go/v1" 38 "github.com/opentracing/opentracing-go" 39 "github.com/pkg/errors" 40 "github.com/sirupsen/logrus" 41 ) 42 43 // Image is the primary struct for dealing with images 44 // It is still very much a work in progress 45 type Image struct { 46 // Adding these two structs for now but will cull when we near 47 // completion of this library. 48 imgRef types.Image 49 imgSrcRef types.ImageSource 50 inspect.ImageData 51 inspect.ImageResult 52 inspectInfo *types.ImageInspectInfo 53 InputName string 54 image *storage.Image 55 imageruntime *Runtime 56 } 57 58 // Runtime contains the store 59 type Runtime struct { 60 store storage.Store 61 SignaturePolicyPath string 62 EventsLogFilePath string 63 EventsLogger string 64 Eventer events.Eventer 65 } 66 67 // InfoImage keep information of Image along with all associated layers 68 type InfoImage struct { 69 // ID of image 70 ID string 71 // Tags of image 72 Tags []string 73 // Layers stores all layers of image. 74 Layers []LayerInfo 75 } 76 77 // ImageFilter is a function to determine whether a image is included 78 // in command output. Images to be outputted are tested using the function. 79 // A true return will include the image, a false return will exclude it. 80 type ImageFilter func(*Image) bool //nolint 81 82 // ErrRepoTagNotFound is the error returned when the image id given doesn't match a rep tag in store 83 var ErrRepoTagNotFound = stderrors.New("unable to match user input to any specific repotag") 84 85 // ErrImageIsBareList is the error returned when the image is just a list or index 86 var ErrImageIsBareList = stderrors.New("image contains a manifest list or image index, but no runnable image") 87 88 // NewImageRuntimeFromStore creates an ImageRuntime based on a provided store 89 func NewImageRuntimeFromStore(store storage.Store) *Runtime { 90 return &Runtime{ 91 store: store, 92 } 93 } 94 95 // NewImageRuntimeFromOptions creates an Image Runtime including the store given 96 // store options 97 func NewImageRuntimeFromOptions(options storage.StoreOptions) (*Runtime, error) { 98 store, err := setStore(options) 99 if err != nil { 100 return nil, err 101 } 102 return NewImageRuntimeFromStore(store), nil 103 } 104 105 func setStore(options storage.StoreOptions) (storage.Store, error) { 106 store, err := storage.GetStore(options) 107 if err != nil { 108 return nil, err 109 } 110 is.Transport.SetStore(store) 111 return store, nil 112 } 113 114 // newImage creates a new image object given an "input name" and a storage.Image 115 func (ir *Runtime) newImage(inputName string, img *storage.Image) *Image { 116 return &Image{ 117 InputName: inputName, 118 imageruntime: ir, 119 image: img, 120 } 121 } 122 123 // newFromStorage creates a new image object from a storage.Image. Its "input name" will be its ID. 124 func (ir *Runtime) newFromStorage(img *storage.Image) *Image { 125 return ir.newImage(img.ID, img) 126 } 127 128 // NewFromLocal creates a new image object that is intended 129 // to only deal with local images already in the store (or 130 // its aliases) 131 func (ir *Runtime) NewFromLocal(name string) (*Image, error) { 132 updatedInputName, localImage, err := ir.getLocalImage(name) 133 if err != nil { 134 return nil, err 135 } 136 return ir.newImage(updatedInputName, localImage), nil 137 } 138 139 // New creates a new image object where the image could be local 140 // or remote 141 func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile string, writer io.Writer, dockeroptions *DockerRegistryOptions, signingoptions SigningOptions, label *string, pullType util.PullType) (*Image, error) { 142 span, _ := opentracing.StartSpanFromContext(ctx, "newImage") 143 span.SetTag("type", "runtime") 144 defer span.Finish() 145 146 // We don't know if the image is local or not ... check local first 147 if pullType != util.PullImageAlways { 148 newImage, err := ir.NewFromLocal(name) 149 if err == nil { 150 return newImage, nil 151 } else if pullType == util.PullImageNever { 152 return nil, err 153 } 154 } 155 156 // The image is not local 157 if signaturePolicyPath == "" { 158 signaturePolicyPath = ir.SignaturePolicyPath 159 } 160 imageName, err := ir.pullImageFromHeuristicSource(ctx, name, writer, authfile, signaturePolicyPath, signingoptions, dockeroptions, label) 161 if err != nil { 162 return nil, errors.Wrapf(err, "unable to pull %s", name) 163 } 164 165 newImage, err := ir.NewFromLocal(imageName[0]) 166 if err != nil { 167 return nil, errors.Wrapf(err, "error retrieving local image after pulling %s", name) 168 } 169 return newImage, nil 170 } 171 172 // LoadFromArchiveReference creates a new image object for images pulled from a tar archive and the like (podman load) 173 // This function is needed because it is possible for a tar archive to have multiple tags for one image 174 func (ir *Runtime) LoadFromArchiveReference(ctx context.Context, srcRef types.ImageReference, signaturePolicyPath string, writer io.Writer) ([]*Image, error) { 175 var newImages []*Image 176 177 if signaturePolicyPath == "" { 178 signaturePolicyPath = ir.SignaturePolicyPath 179 } 180 imageNames, err := ir.pullImageFromReference(ctx, srcRef, writer, "", signaturePolicyPath, SigningOptions{}, &DockerRegistryOptions{}) 181 if err != nil { 182 return nil, errors.Wrapf(err, "unable to pull %s", transports.ImageName(srcRef)) 183 } 184 185 for _, name := range imageNames { 186 newImage, err := ir.NewFromLocal(name) 187 if err != nil { 188 return nil, errors.Wrapf(err, "error retrieving local image after pulling %s", name) 189 } 190 newImages = append(newImages, newImage) 191 } 192 ir.newImageEvent(events.LoadFromArchive, "") 193 return newImages, nil 194 } 195 196 // Shutdown closes down the storage and require a bool arg as to 197 // whether it should do so forcibly. 198 func (ir *Runtime) Shutdown(force bool) error { 199 _, err := ir.store.Shutdown(force) 200 return err 201 } 202 203 // GetImagesWithFilters gets images with a series of filters applied 204 func (ir *Runtime) GetImagesWithFilters(filters []string) ([]*Image, error) { 205 filterFuncs, err := ir.createFilterFuncs(filters, nil) 206 if err != nil { 207 return nil, err 208 } 209 images, err := ir.GetImages() 210 if err != nil { 211 return nil, err 212 } 213 return FilterImages(images, filterFuncs), nil 214 } 215 216 func (i *Image) reloadImage() error { 217 newImage, err := i.imageruntime.getImage(i.ID()) 218 if err != nil { 219 return errors.Wrapf(err, "unable to reload image") 220 } 221 i.image = newImage 222 return nil 223 } 224 225 // stringSha256 strips sha256 from user input 226 func stripSha256(name string) string { 227 if strings.HasPrefix(name, "sha256:") && len(name) > 7 { 228 return name[7:] 229 } 230 return name 231 } 232 233 // getLocalImage resolves an unknown input describing an image and 234 // returns an updated input name, and a storage.Image, or an error. It is used by NewFromLocal. 235 func (ir *Runtime) getLocalImage(inputName string) (string, *storage.Image, error) { 236 imageError := fmt.Sprintf("unable to find '%s' in local storage", inputName) 237 if inputName == "" { 238 return "", nil, errors.Errorf("input name is blank") 239 } 240 // Check if the input name has a transport and if so strip it 241 dest, err := alltransports.ParseImageName(inputName) 242 if err == nil && dest.DockerReference() != nil { 243 inputName = dest.DockerReference().String() 244 } 245 246 img, err := ir.getImage(stripSha256(inputName)) 247 if err == nil { 248 return inputName, img, err 249 } 250 251 // container-storage wasn't able to find it in its current form 252 // check if the input name has a tag, and if not, run it through 253 // again 254 decomposedImage, err := decompose(inputName) 255 if err != nil { 256 return "", nil, err 257 } 258 259 // The image has a registry name in it and we made sure we looked for it locally 260 // with a tag. It cannot be local. 261 if decomposedImage.hasRegistry { 262 return "", nil, errors.Wrapf(ErrNoSuchImage, imageError) 263 } 264 // if the image is saved with the repository localhost, searching with localhost prepended is necessary 265 // We don't need to strip the sha because we have already determined it is not an ID 266 ref, err := decomposedImage.referenceWithRegistry(DefaultLocalRegistry) 267 if err != nil { 268 return "", nil, err 269 } 270 img, err = ir.getImage(ref.String()) 271 if err == nil { 272 return inputName, img, err 273 } 274 275 // grab all the local images 276 images, err := ir.GetImages() 277 if err != nil { 278 return "", nil, err 279 } 280 281 // check the repotags of all images for a match 282 repoImage, err := findImageInRepotags(decomposedImage, images) 283 if err == nil { 284 return inputName, repoImage, nil 285 } 286 287 return "", nil, errors.Wrapf(ErrNoSuchImage, err.Error()) 288 } 289 290 // ID returns the image ID as a string 291 func (i *Image) ID() string { 292 return i.image.ID 293 } 294 295 // IsReadOnly returns whether the image ID comes from a local store 296 func (i *Image) IsReadOnly() bool { 297 return i.image.ReadOnly 298 } 299 300 // Digest returns the image's digest 301 func (i *Image) Digest() digest.Digest { 302 return i.image.Digest 303 } 304 305 // Digests returns the image's digests 306 func (i *Image) Digests() []digest.Digest { 307 return i.image.Digests 308 } 309 310 // GetManifest returns the image's manifest as a byte array 311 // and manifest type as a string. 312 func (i *Image) GetManifest(ctx context.Context, instanceDigest *digest.Digest) ([]byte, string, error) { 313 imgSrcRef, err := i.toImageSourceRef(ctx) 314 if err != nil { 315 return nil, "", err 316 } 317 return imgSrcRef.GetManifest(ctx, instanceDigest) 318 } 319 320 // Manifest returns the image's manifest as a byte array 321 // and manifest type as a string. 322 func (i *Image) Manifest(ctx context.Context) ([]byte, string, error) { 323 imgRef, err := i.toImageRef(ctx) 324 if err != nil { 325 return nil, "", err 326 } 327 return imgRef.Manifest(ctx) 328 } 329 330 // Names returns a string array of names associated with the image, which may be a mixture of tags and digests 331 func (i *Image) Names() []string { 332 return i.image.Names 333 } 334 335 // NamesHistory returns a string array of names previously associated with the 336 // image, which may be a mixture of tags and digests 337 func (i *Image) NamesHistory() []string { 338 if len(i.image.Names) > 0 && len(i.image.NamesHistory) > 0 && 339 // We compare the latest (time-referenced) tags for equality and skip 340 // it in the history if they match to not display them twice. We have 341 // to compare like this, because `i.image.Names` (latest last) gets 342 // appended on retag, whereas `i.image.NamesHistory` gets prepended 343 // (latest first) 344 i.image.Names[len(i.image.Names)-1] == i.image.NamesHistory[0] { 345 return i.image.NamesHistory[1:] 346 } 347 return i.image.NamesHistory 348 } 349 350 // RepoTags returns a string array of repotags associated with the image 351 func (i *Image) RepoTags() ([]string, error) { 352 var repoTags []string 353 for _, name := range i.Names() { 354 named, err := reference.ParseNormalizedNamed(name) 355 if err != nil { 356 return nil, err 357 } 358 if tagged, isTagged := named.(reference.NamedTagged); isTagged { 359 repoTags = append(repoTags, tagged.String()) 360 } 361 } 362 return repoTags, nil 363 } 364 365 // RepoDigests returns a string array of repodigests associated with the image 366 func (i *Image) RepoDigests() ([]string, error) { 367 var repoDigests []string 368 added := make(map[string]struct{}) 369 370 for _, name := range i.Names() { 371 for _, imageDigest := range append(i.Digests(), i.Digest()) { 372 if imageDigest == "" { 373 continue 374 } 375 376 named, err := reference.ParseNormalizedNamed(name) 377 if err != nil { 378 return nil, err 379 } 380 381 canonical, err := reference.WithDigest(reference.TrimNamed(named), imageDigest) 382 if err != nil { 383 return nil, err 384 } 385 386 if _, alreadyInList := added[canonical.String()]; !alreadyInList { 387 repoDigests = append(repoDigests, canonical.String()) 388 added[canonical.String()] = struct{}{} 389 } 390 } 391 } 392 sort.Strings(repoDigests) 393 return repoDigests, nil 394 } 395 396 // Created returns the time the image was created 397 func (i *Image) Created() time.Time { 398 return i.image.Created 399 } 400 401 // TopLayer returns the top layer id as a string 402 func (i *Image) TopLayer() string { 403 return i.image.TopLayer 404 } 405 406 // Remove an image; container removal for the image must be done 407 // outside the context of images 408 // TODO: the force param does nothing as of now. Need to move container 409 // handling logic here eventually. 410 func (i *Image) Remove(ctx context.Context, force bool) error { 411 parent, err := i.GetParent(ctx) 412 if err != nil { 413 return err 414 } 415 if _, err := i.imageruntime.store.DeleteImage(i.ID(), true); err != nil { 416 return err 417 } 418 i.newImageEvent(events.Remove) 419 for parent != nil { 420 nextParent, err := parent.GetParent(ctx) 421 if err != nil { 422 return err 423 } 424 children, err := parent.GetChildren(ctx) 425 if err != nil { 426 return err 427 } 428 // Do not remove if image is a base image and is not untagged, or if 429 // the image has more children. 430 if len(children) > 0 || len(parent.Names()) > 0 { 431 return nil 432 } 433 id := parent.ID() 434 if _, err := i.imageruntime.store.DeleteImage(id, true); err != nil { 435 logrus.Debugf("unable to remove intermediate image %q: %v", id, err) 436 } else { 437 fmt.Println(id) 438 } 439 parent = nextParent 440 } 441 return nil 442 } 443 444 // getImage retrieves an image matching the given name or hash from system 445 // storage 446 // If no matching image can be found, an error is returned 447 func (ir *Runtime) getImage(image string) (*storage.Image, error) { 448 var img *storage.Image 449 ref, err := is.Transport.ParseStoreReference(ir.store, image) 450 if err == nil { 451 img, err = is.Transport.GetStoreImage(ir.store, ref) 452 } 453 if err != nil { 454 img2, err2 := ir.store.Image(image) 455 if err2 != nil { 456 if ref == nil { 457 return nil, errors.Wrapf(err, "error parsing reference to image %q", image) 458 } 459 return nil, errors.Wrapf(err, "unable to locate image %q", image) 460 } 461 img = img2 462 } 463 return img, nil 464 } 465 466 // GetImages retrieves all images present in storage 467 func (ir *Runtime) GetImages() ([]*Image, error) { 468 return ir.getImages(false) 469 } 470 471 // GetRWImages retrieves all read/write images present in storage 472 func (ir *Runtime) GetRWImages() ([]*Image, error) { 473 return ir.getImages(true) 474 } 475 476 // getImages retrieves all images present in storage 477 func (ir *Runtime) getImages(rwOnly bool) ([]*Image, error) { 478 var newImages []*Image 479 images, err := ir.store.Images() 480 if err != nil { 481 return nil, err 482 } 483 for _, i := range images { 484 if rwOnly && i.ReadOnly { 485 continue 486 } 487 // iterating over these, be careful to not iterate on the literal 488 // pointer. 489 image := i 490 img := ir.newFromStorage(&image) 491 newImages = append(newImages, img) 492 } 493 return newImages, nil 494 } 495 496 // getImageDigest creates an image object and uses the hex value of the digest as the image ID 497 // for parsing the store reference 498 func getImageDigest(ctx context.Context, src types.ImageReference, sc *types.SystemContext) (string, error) { 499 newImg, err := src.NewImage(ctx, sc) 500 if err != nil { 501 return "", err 502 } 503 defer func() { 504 if err := newImg.Close(); err != nil { 505 logrus.Errorf("failed to close image: %q", err) 506 } 507 }() 508 imageDigest := newImg.ConfigInfo().Digest 509 if err = imageDigest.Validate(); err != nil { 510 return "", errors.Wrapf(err, "error getting config info") 511 } 512 return "@" + imageDigest.Hex(), nil 513 } 514 515 // NormalizedTag returns the canonical version of tag for use in Image.Names() 516 func NormalizedTag(tag string) (reference.Named, error) { 517 decomposedTag, err := decompose(tag) 518 if err != nil { 519 return nil, err 520 } 521 // If the input doesn't specify a registry, set the registry to localhost 522 var ref reference.Named 523 if !decomposedTag.hasRegistry { 524 ref, err = decomposedTag.referenceWithRegistry(DefaultLocalRegistry) 525 if err != nil { 526 return nil, err 527 } 528 } else { 529 ref, err = decomposedTag.normalizedReference() 530 if err != nil { 531 return nil, err 532 } 533 } 534 // If the input does not have a tag, we need to add one (latest) 535 ref = reference.TagNameOnly(ref) 536 return ref, nil 537 } 538 539 // TagImage adds a tag to the given image 540 func (i *Image) TagImage(tag string) error { 541 if err := i.reloadImage(); err != nil { 542 return err 543 } 544 ref, err := NormalizedTag(tag) 545 if err != nil { 546 return err 547 } 548 tags := i.Names() 549 if util.StringInSlice(ref.String(), tags) { 550 return nil 551 } 552 tags = append(tags, ref.String()) 553 if err := i.imageruntime.store.SetNames(i.ID(), tags); err != nil { 554 return err 555 } 556 if err := i.reloadImage(); err != nil { 557 return err 558 } 559 i.newImageEvent(events.Tag) 560 return nil 561 } 562 563 // UntagImage removes a tag from the given image 564 func (i *Image) UntagImage(tag string) error { 565 if err := i.reloadImage(); err != nil { 566 return err 567 } 568 var newTags []string 569 tags := i.Names() 570 if !util.StringInSlice(tag, tags) { 571 return nil 572 } 573 for _, t := range tags { 574 if tag != t { 575 newTags = append(newTags, t) 576 } 577 } 578 if err := i.imageruntime.store.SetNames(i.ID(), newTags); err != nil { 579 return err 580 } 581 if err := i.reloadImage(); err != nil { 582 return err 583 } 584 i.newImageEvent(events.Untag) 585 return nil 586 } 587 588 // PushImageToHeuristicDestination pushes the given image to "destination", which is heuristically parsed. 589 // Use PushImageToReference if the destination is known precisely. 590 func (i *Image) PushImageToHeuristicDestination(ctx context.Context, destination, manifestMIMEType, authFile, digestFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { 591 if destination == "" { 592 return errors.Wrapf(syscall.EINVAL, "destination image name must be specified") 593 } 594 595 // Get the destination Image Reference 596 dest, err := alltransports.ParseImageName(destination) 597 if err != nil { 598 if hasTransport(destination) { 599 return errors.Wrapf(err, "error getting destination imageReference for %q", destination) 600 } 601 // Try adding the images default transport 602 destination2 := DefaultTransport + destination 603 dest, err = alltransports.ParseImageName(destination2) 604 if err != nil { 605 return err 606 } 607 } 608 return i.PushImageToReference(ctx, dest, manifestMIMEType, authFile, digestFile, signaturePolicyPath, writer, forceCompress, signingOptions, dockerRegistryOptions, additionalDockerArchiveTags) 609 } 610 611 // PushImageToReference pushes the given image to a location described by the given path 612 func (i *Image) PushImageToReference(ctx context.Context, dest types.ImageReference, manifestMIMEType, authFile, digestFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { 613 sc := GetSystemContext(signaturePolicyPath, authFile, forceCompress) 614 sc.BlobInfoCacheDir = filepath.Join(i.imageruntime.store.GraphRoot(), "cache") 615 616 policyContext, err := getPolicyContext(sc) 617 if err != nil { 618 return err 619 } 620 defer func() { 621 if err := policyContext.Destroy(); err != nil { 622 logrus.Errorf("failed to destroy policy context: %q", err) 623 } 624 }() 625 626 // Look up the source image, expecting it to be in local storage 627 src, err := is.Transport.ParseStoreReference(i.imageruntime.store, i.ID()) 628 if err != nil { 629 return errors.Wrapf(err, "error getting source imageReference for %q", i.InputName) 630 } 631 copyOptions := getCopyOptions(sc, writer, nil, dockerRegistryOptions, signingOptions, manifestMIMEType, additionalDockerArchiveTags) 632 copyOptions.DestinationCtx.SystemRegistriesConfPath = registries.SystemRegistriesConfPath() // FIXME: Set this more globally. Probably no reason not to have it in every types.SystemContext, and to compute the value just once in one place. 633 // Copy the image to the remote destination 634 manifestBytes, err := cp.Image(ctx, policyContext, dest, src, copyOptions) 635 if err != nil { 636 return errors.Wrapf(err, "Error copying image to the remote destination") 637 } 638 digest, err := manifest.Digest(manifestBytes) 639 if err != nil { 640 return errors.Wrapf(err, "error computing digest of manifest of new image %q", transports.ImageName(dest)) 641 } 642 643 logrus.Debugf("Successfully pushed %s with digest %s", transports.ImageName(dest), digest.String()) 644 645 if digestFile != "" { 646 if err = ioutil.WriteFile(digestFile, []byte(digest.String()), 0644); err != nil { 647 return errors.Wrapf(err, "failed to write digest to file %q", digestFile) 648 } 649 } 650 i.newImageEvent(events.Push) 651 return nil 652 } 653 654 // MatchesID returns a bool based on if the input id 655 // matches the image's id 656 // TODO: This isn't used anywhere, so remove it 657 func (i *Image) MatchesID(id string) bool { 658 return strings.HasPrefix(i.ID(), id) 659 } 660 661 // ToImageRef returns an image reference type from an image 662 // TODO: Hopefully we can remove this exported function for mheon 663 func (i *Image) ToImageRef(ctx context.Context) (types.Image, error) { 664 return i.toImageRef(ctx) 665 } 666 667 // toImageSourceRef returns an ImageSource Reference type from an image 668 func (i *Image) toImageSourceRef(ctx context.Context) (types.ImageSource, error) { 669 if i == nil { 670 return nil, errors.Errorf("cannot convert nil image to image source reference") 671 } 672 if i.imgSrcRef == nil { 673 ref, err := is.Transport.ParseStoreReference(i.imageruntime.store, "@"+i.ID()) 674 if err != nil { 675 return nil, errors.Wrapf(err, "error parsing reference to image %q", i.ID()) 676 } 677 imgSrcRef, err := ref.NewImageSource(ctx, nil) 678 if err != nil { 679 return nil, errors.Wrapf(err, "error reading image %q as image source", i.ID()) 680 } 681 i.imgSrcRef = imgSrcRef 682 } 683 return i.imgSrcRef, nil 684 } 685 686 //Size returns the size of the image 687 func (i *Image) Size(ctx context.Context) (*uint64, error) { 688 sum, err := i.imageruntime.store.ImageSize(i.ID()) 689 if err == nil && sum >= 0 { 690 usum := uint64(sum) 691 return &usum, nil 692 } 693 return nil, errors.Wrap(err, "unable to determine size") 694 } 695 696 // toImageRef returns an Image Reference type from an image 697 func (i *Image) toImageRef(ctx context.Context) (types.Image, error) { 698 if i == nil { 699 return nil, errors.Errorf("cannot convert nil image to image reference") 700 } 701 imgSrcRef, err := i.toImageSourceRef(ctx) 702 if err != nil { 703 return nil, err 704 } 705 if i.imgRef == nil { 706 systemContext := &types.SystemContext{} 707 unparsedDefaultInstance := image.UnparsedInstance(imgSrcRef, nil) 708 imgRef, err := image.FromUnparsedImage(ctx, systemContext, unparsedDefaultInstance) 709 if err != nil { 710 // check for a "tried-to-treat-a-bare-list-like-a-runnable-image" problem, else 711 // return info about the not-a-bare-list runnable image part of this storage.Image 712 if manifestBytes, manifestType, err2 := imgSrcRef.GetManifest(ctx, nil); err2 == nil { 713 if manifest.MIMETypeIsMultiImage(manifestType) { 714 if list, err3 := manifest.ListFromBlob(manifestBytes, manifestType); err3 == nil { 715 switch manifestType { 716 case ociv1.MediaTypeImageIndex: 717 err = errors.Wrapf(ErrImageIsBareList, "%q is an image index", i.InputName) 718 case manifest.DockerV2ListMediaType: 719 err = errors.Wrapf(ErrImageIsBareList, "%q is a manifest list", i.InputName) 720 default: 721 err = errors.Wrapf(ErrImageIsBareList, "%q", i.InputName) 722 } 723 for _, instanceDigest := range list.Instances() { 724 instance := instanceDigest 725 unparsedInstance := image.UnparsedInstance(imgSrcRef, &instance) 726 if imgRef2, err4 := image.FromUnparsedImage(ctx, systemContext, unparsedInstance); err4 == nil { 727 imgRef = imgRef2 728 err = nil 729 break 730 } 731 } 732 } 733 } 734 } 735 if err != nil { 736 return nil, errors.Wrapf(err, "error reading image %q as image", i.ID()) 737 } 738 } 739 i.imgRef = imgRef 740 } 741 return i.imgRef, nil 742 } 743 744 // DriverData gets the driver data from the store on a layer 745 func (i *Image) DriverData() (*driver.Data, error) { 746 return driver.GetDriverData(i.imageruntime.store, i.TopLayer()) 747 } 748 749 // Layer returns the image's top layer 750 func (i *Image) Layer() (*storage.Layer, error) { 751 return i.imageruntime.store.Layer(i.image.TopLayer) 752 } 753 754 // History contains the history information of an image 755 type History struct { 756 ID string `json:"id"` 757 Created *time.Time `json:"created"` 758 CreatedBy string `json:"createdBy"` 759 Size int64 `json:"size"` 760 Comment string `json:"comment"` 761 Tags []string `json:"tags"` 762 } 763 764 // History gets the history of an image and the IDs of images that are part of 765 // its history 766 func (i *Image) History(ctx context.Context) ([]*History, error) { 767 img, err := i.toImageRef(ctx) 768 if err != nil { 769 if errors.Cause(err) == ErrImageIsBareList { 770 return nil, nil 771 } 772 return nil, err 773 } 774 oci, err := img.OCIConfig(ctx) 775 if err != nil { 776 return nil, err 777 } 778 779 // Build a mapping from top-layer to image ID. 780 images, err := i.imageruntime.GetImages() 781 if err != nil { 782 return nil, err 783 } 784 topLayerMap := make(map[string]string) 785 for _, image := range images { 786 if _, exists := topLayerMap[image.TopLayer()]; !exists { 787 topLayerMap[image.TopLayer()] = image.ID() 788 } 789 } 790 791 var allHistory []*History 792 var layer *storage.Layer 793 794 // Check if we have an actual top layer to prevent lookup errors. 795 if i.TopLayer() != "" { 796 layer, err = i.imageruntime.store.Layer(i.TopLayer()) 797 if err != nil { 798 return nil, err 799 } 800 } 801 802 // Iterate in reverse order over the history entries, and lookup the 803 // corresponding image ID, size and get the next later if needed. 804 numHistories := len(oci.History) - 1 805 for x := numHistories; x >= 0; x-- { 806 var size int64 807 808 id := "<missing>" 809 if x == numHistories { 810 id = i.ID() 811 } 812 if layer != nil { 813 if !oci.History[x].EmptyLayer { 814 size = layer.UncompressedSize 815 } 816 if imageID, exists := topLayerMap[layer.ID]; exists { 817 id = imageID 818 // Delete the entry to avoid reusing it for following history items. 819 delete(topLayerMap, layer.ID) 820 } 821 } 822 h := History{ 823 ID: id, 824 Created: oci.History[x].Created, 825 CreatedBy: oci.History[x].CreatedBy, 826 Size: size, 827 Comment: oci.History[x].Comment, 828 } 829 if layer != nil { 830 h.Tags = layer.Names 831 } 832 allHistory = append(allHistory, &h) 833 834 if layer != nil && layer.Parent != "" && !oci.History[x].EmptyLayer { 835 layer, err = i.imageruntime.store.Layer(layer.Parent) 836 if err != nil { 837 return nil, err 838 } 839 } 840 } 841 842 return allHistory, nil 843 } 844 845 // Dangling returns a bool if the image is "dangling" 846 func (i *Image) Dangling() bool { 847 return len(i.Names()) == 0 848 } 849 850 // Labels returns the image's labels 851 func (i *Image) Labels(ctx context.Context) (map[string]string, error) { 852 imgInspect, err := i.imageInspectInfo(ctx) 853 if err != nil { 854 return nil, nil 855 } 856 return imgInspect.Labels, nil 857 } 858 859 // GetLabel Returns a case-insensitive match of a given label 860 func (i *Image) GetLabel(ctx context.Context, label string) (string, error) { 861 imageLabels, err := i.Labels(ctx) 862 if err != nil { 863 return "", err 864 } 865 for k, v := range imageLabels { 866 if strings.ToLower(k) == strings.ToLower(label) { 867 return v, nil 868 } 869 } 870 return "", nil 871 } 872 873 // Annotations returns the annotations of an image 874 func (i *Image) Annotations(ctx context.Context) (map[string]string, error) { 875 imageManifest, manifestType, err := i.Manifest(ctx) 876 if err != nil { 877 imageManifest, manifestType, err = i.GetManifest(ctx, nil) 878 if err != nil { 879 return nil, err 880 } 881 } 882 annotations := make(map[string]string) 883 if manifestType == ociv1.MediaTypeImageManifest { 884 var m ociv1.Manifest 885 if err := json.Unmarshal(imageManifest, &m); err == nil { 886 for k, v := range m.Annotations { 887 annotations[k] = v 888 } 889 } 890 } 891 return annotations, nil 892 } 893 894 // ociv1Image converts an image to an imgref and then returns its config blob 895 // converted to an ociv1 image type 896 func (i *Image) ociv1Image(ctx context.Context) (*ociv1.Image, error) { 897 imgRef, err := i.toImageRef(ctx) 898 if err != nil { 899 return nil, err 900 } 901 return imgRef.OCIConfig(ctx) 902 } 903 904 func (i *Image) imageInspectInfo(ctx context.Context) (*types.ImageInspectInfo, error) { 905 if i.inspectInfo == nil { 906 ic, err := i.toImageRef(ctx) 907 if err != nil { 908 return nil, err 909 } 910 imgInspect, err := ic.Inspect(ctx) 911 if err != nil { 912 return nil, err 913 } 914 i.inspectInfo = imgInspect 915 } 916 return i.inspectInfo, nil 917 } 918 919 func (i *Image) inspect(ctx context.Context, calculateSize bool) (*inspect.ImageData, error) { 920 ociv1Img, err := i.ociv1Image(ctx) 921 if err != nil { 922 ociv1Img = &ociv1.Image{} 923 } 924 info, err := i.imageInspectInfo(ctx) 925 if err != nil { 926 info = &types.ImageInspectInfo{} 927 } 928 annotations, err := i.Annotations(ctx) 929 if err != nil { 930 return nil, err 931 } 932 933 size := int64(-1) 934 if calculateSize { 935 if usize, err := i.Size(ctx); err == nil { 936 size = int64(*usize) 937 } 938 } 939 940 repoTags, err := i.RepoTags() 941 if err != nil { 942 return nil, err 943 } 944 945 repoDigests, err := i.RepoDigests() 946 if err != nil { 947 return nil, err 948 } 949 950 driver, err := i.DriverData() 951 if err != nil { 952 return nil, err 953 } 954 955 _, manifestType, err := i.GetManifest(ctx, nil) 956 if err != nil { 957 return nil, errors.Wrapf(err, "unable to determine manifest type") 958 } 959 comment, err := i.Comment(ctx, manifestType) 960 if err != nil { 961 return nil, err 962 } 963 964 data := &inspect.ImageData{ 965 ID: i.ID(), 966 RepoTags: repoTags, 967 RepoDigests: repoDigests, 968 Comment: comment, 969 Created: ociv1Img.Created, 970 Author: ociv1Img.Author, 971 Architecture: ociv1Img.Architecture, 972 Os: ociv1Img.OS, 973 Config: &ociv1Img.Config, 974 Version: info.DockerVersion, 975 Size: size, 976 VirtualSize: size, 977 Annotations: annotations, 978 Digest: i.Digest(), 979 Labels: info.Labels, 980 RootFS: &inspect.RootFS{ 981 Type: ociv1Img.RootFS.Type, 982 Layers: ociv1Img.RootFS.DiffIDs, 983 }, 984 GraphDriver: driver, 985 ManifestType: manifestType, 986 User: ociv1Img.Config.User, 987 History: ociv1Img.History, 988 NamesHistory: i.NamesHistory(), 989 } 990 if manifestType == manifest.DockerV2Schema2MediaType { 991 hc, err := i.GetHealthCheck(ctx) 992 if err != nil { 993 return nil, err 994 } 995 if hc != nil { 996 data.HealthCheck = hc 997 } 998 } 999 return data, nil 1000 } 1001 1002 // Inspect returns an image's inspect data 1003 func (i *Image) Inspect(ctx context.Context) (*inspect.ImageData, error) { 1004 span, _ := opentracing.StartSpanFromContext(ctx, "imageInspect") 1005 1006 span.SetTag("type", "image") 1007 defer span.Finish() 1008 1009 return i.inspect(ctx, true) 1010 } 1011 1012 // InspectNoSize returns an image's inspect data without calculating the size for the image 1013 func (i *Image) InspectNoSize(ctx context.Context) (*inspect.ImageData, error) { 1014 span, _ := opentracing.StartSpanFromContext(ctx, "imageInspectNoSize") 1015 1016 span.SetTag("type", "image") 1017 defer span.Finish() 1018 1019 return i.inspect(ctx, false) 1020 } 1021 1022 // Import imports and image into the store and returns an image 1023 func (ir *Runtime) Import(ctx context.Context, path, reference string, writer io.Writer, signingOptions SigningOptions, imageConfig ociv1.Image) (*Image, error) { 1024 src, err := tarball.Transport.ParseReference(path) 1025 if err != nil { 1026 return nil, errors.Wrapf(err, "error parsing image name %q", path) 1027 } 1028 1029 updater, ok := src.(tarball.ConfigUpdater) 1030 if !ok { 1031 return nil, errors.Wrapf(err, "unexpected type, a tarball reference should implement tarball.ConfigUpdater") 1032 } 1033 1034 annotations := make(map[string]string) 1035 1036 // config imgspecv1.Image 1037 err = updater.ConfigUpdate(imageConfig, annotations) 1038 if err != nil { 1039 return nil, errors.Wrapf(err, "error updating image config") 1040 } 1041 1042 sc := GetSystemContext("", "", false) 1043 1044 // if reference not given, get the image digest 1045 if reference == "" { 1046 reference, err = getImageDigest(ctx, src, sc) 1047 if err != nil { 1048 return nil, err 1049 } 1050 } 1051 policyContext, err := getPolicyContext(sc) 1052 if err != nil { 1053 return nil, err 1054 } 1055 defer func() { 1056 if err := policyContext.Destroy(); err != nil { 1057 logrus.Errorf("failed to destroy policy context: %q", err) 1058 } 1059 }() 1060 copyOptions := getCopyOptions(sc, writer, nil, nil, signingOptions, "", nil) 1061 dest, err := is.Transport.ParseStoreReference(ir.store, reference) 1062 if err != nil { 1063 return nil, errors.Wrapf(err, "error getting image reference for %q", reference) 1064 } 1065 _, err = cp.Image(ctx, policyContext, dest, src, copyOptions) 1066 if err != nil { 1067 return nil, err 1068 } 1069 newImage, err := ir.NewFromLocal(reference) 1070 if err == nil { 1071 newImage.newImageEvent(events.Import) 1072 } 1073 return newImage, err 1074 } 1075 1076 // MatchRepoTag takes a string and tries to match it against an 1077 // image's repotags 1078 func (i *Image) MatchRepoTag(input string) (string, error) { 1079 results := make(map[int][]string) 1080 var maxCount int 1081 // first check if we have an exact match with the input 1082 if util.StringInSlice(input, i.Names()) { 1083 return input, nil 1084 } 1085 // next check if we are missing the tag 1086 dcImage, err := decompose(input) 1087 if err != nil { 1088 return "", err 1089 } 1090 imageRegistry, imageName, imageSuspiciousTagValueForSearch := dcImage.suspiciousRefNameTagValuesForSearch() 1091 for _, repoName := range i.Names() { 1092 count := 0 1093 dcRepoName, err := decompose(repoName) 1094 if err != nil { 1095 return "", err 1096 } 1097 repoNameRegistry, repoNameName, repoNameSuspiciousTagValueForSearch := dcRepoName.suspiciousRefNameTagValuesForSearch() 1098 if repoNameRegistry == imageRegistry && imageRegistry != "" { 1099 count++ 1100 } 1101 if repoNameName == imageName && imageName != "" { 1102 count++ 1103 } else if splitString(repoNameName) == splitString(imageName) { 1104 count++ 1105 } 1106 if repoNameSuspiciousTagValueForSearch == imageSuspiciousTagValueForSearch { 1107 count++ 1108 } 1109 results[count] = append(results[count], repoName) 1110 if count > maxCount { 1111 maxCount = count 1112 } 1113 } 1114 if maxCount == 0 { 1115 return "", ErrRepoTagNotFound 1116 } 1117 if len(results[maxCount]) > 1 { 1118 return "", errors.Errorf("user input matched multiple repotags for the image") 1119 } 1120 return results[maxCount][0], nil 1121 } 1122 1123 // splitString splits input string by / and returns the last array item 1124 func splitString(input string) string { 1125 split := strings.Split(input, "/") 1126 return split[len(split)-1] 1127 } 1128 1129 // IsParent goes through the layers in the store and checks if i.TopLayer is 1130 // the parent of any other layer in store. Double check that image with that 1131 // layer exists as well. 1132 func (i *Image) IsParent(ctx context.Context) (bool, error) { 1133 children, err := i.getChildren(ctx, 1) 1134 if err != nil { 1135 if errors.Cause(err) == ErrImageIsBareList { 1136 return false, nil 1137 } 1138 return false, err 1139 } 1140 return len(children) > 0, nil 1141 } 1142 1143 // historiesMatch returns the number of entries in the histories which have the 1144 // same contents 1145 func historiesMatch(a, b []imgspecv1.History) int { 1146 i := 0 1147 for i < len(a) && i < len(b) { 1148 if a[i].Created != nil && b[i].Created == nil { 1149 return i 1150 } 1151 if a[i].Created == nil && b[i].Created != nil { 1152 return i 1153 } 1154 if a[i].Created != nil && b[i].Created != nil { 1155 if !a[i].Created.Equal(*(b[i].Created)) { 1156 return i 1157 } 1158 } 1159 if a[i].CreatedBy != b[i].CreatedBy { 1160 return i 1161 } 1162 if a[i].Author != b[i].Author { 1163 return i 1164 } 1165 if a[i].Comment != b[i].Comment { 1166 return i 1167 } 1168 if a[i].EmptyLayer != b[i].EmptyLayer { 1169 return i 1170 } 1171 i++ 1172 } 1173 return i 1174 } 1175 1176 // areParentAndChild checks diff ID and history in the two images and return 1177 // true if the second should be considered to be directly based on the first 1178 func areParentAndChild(parent, child *imgspecv1.Image) bool { 1179 // the child and candidate parent should share all of the 1180 // candidate parent's diff IDs, which together would have 1181 // controlled which layers were used 1182 if len(parent.RootFS.DiffIDs) > len(child.RootFS.DiffIDs) { 1183 return false 1184 } 1185 childUsesCandidateDiffs := true 1186 for i := range parent.RootFS.DiffIDs { 1187 if child.RootFS.DiffIDs[i] != parent.RootFS.DiffIDs[i] { 1188 childUsesCandidateDiffs = false 1189 break 1190 } 1191 } 1192 if !childUsesCandidateDiffs { 1193 return false 1194 } 1195 // the child should have the same history as the parent, plus 1196 // one more entry 1197 if len(parent.History)+1 != len(child.History) { 1198 return false 1199 } 1200 if historiesMatch(parent.History, child.History) != len(parent.History) { 1201 return false 1202 } 1203 return true 1204 } 1205 1206 // GetParent returns the image ID of the parent. Return nil if a parent is not found. 1207 func (i *Image) GetParent(ctx context.Context) (*Image, error) { 1208 var childLayer *storage.Layer 1209 images, err := i.imageruntime.GetImages() 1210 if err != nil { 1211 return nil, err 1212 } 1213 if i.TopLayer() != "" { 1214 if childLayer, err = i.imageruntime.store.Layer(i.TopLayer()); err != nil { 1215 return nil, err 1216 } 1217 } 1218 // fetch the configuration for the child image 1219 child, err := i.ociv1Image(ctx) 1220 if err != nil { 1221 if errors.Cause(err) == ErrImageIsBareList { 1222 return nil, nil 1223 } 1224 return nil, err 1225 } 1226 for _, img := range images { 1227 if img.ID() == i.ID() { 1228 continue 1229 } 1230 candidateLayer := img.TopLayer() 1231 // as a child, our top layer, if we have one, is either the 1232 // candidate parent's layer, or one that's derived from it, so 1233 // skip over any candidate image where we know that isn't the 1234 // case 1235 if childLayer != nil { 1236 // The child has at least one layer, so a parent would 1237 // have a top layer that's either the same as the child's 1238 // top layer or the top layer's recorded parent layer, 1239 // which could be an empty value. 1240 if candidateLayer != childLayer.Parent && candidateLayer != childLayer.ID { 1241 continue 1242 } 1243 } else { 1244 // The child has no layers, but the candidate does. 1245 if candidateLayer != "" { 1246 continue 1247 } 1248 } 1249 // fetch the configuration for the candidate image 1250 candidate, err := img.ociv1Image(ctx) 1251 if err != nil { 1252 return nil, err 1253 } 1254 // compare them 1255 if areParentAndChild(candidate, child) { 1256 return img, nil 1257 } 1258 } 1259 return nil, nil 1260 } 1261 1262 // GetChildren returns a list of the imageIDs that depend on the image 1263 func (i *Image) GetChildren(ctx context.Context) ([]string, error) { 1264 children, err := i.getChildren(ctx, 0) 1265 if err != nil { 1266 if errors.Cause(err) == ErrImageIsBareList { 1267 return nil, nil 1268 } 1269 return nil, err 1270 } 1271 return children, nil 1272 } 1273 1274 // getChildren returns a list of at most "max" imageIDs that depend on the image 1275 func (i *Image) getChildren(ctx context.Context, max int) ([]string, error) { 1276 var children []string 1277 1278 if _, err := i.toImageRef(ctx); err != nil { 1279 return nil, nil 1280 } 1281 1282 images, err := i.imageruntime.GetImages() 1283 if err != nil { 1284 return nil, err 1285 } 1286 1287 // fetch the configuration for the parent image 1288 parent, err := i.ociv1Image(ctx) 1289 if err != nil { 1290 return nil, err 1291 } 1292 parentLayer := i.TopLayer() 1293 1294 for _, img := range images { 1295 if img.ID() == i.ID() { 1296 continue 1297 } 1298 if img.TopLayer() == "" { 1299 if parentLayer != "" { 1300 // this image has no layers, but we do, so 1301 // it can't be derived from this one 1302 continue 1303 } 1304 } else { 1305 candidateLayer, err := img.Layer() 1306 if err != nil { 1307 return nil, err 1308 } 1309 // if this image's top layer is not our top layer, and is not 1310 // based on our top layer, we can skip it 1311 if candidateLayer.Parent != parentLayer && candidateLayer.ID != parentLayer { 1312 continue 1313 } 1314 } 1315 // fetch the configuration for the candidate image 1316 candidate, err := img.ociv1Image(ctx) 1317 if err != nil { 1318 return nil, err 1319 } 1320 // compare them 1321 if areParentAndChild(parent, candidate) { 1322 children = append(children, img.ID()) 1323 } 1324 // if we're not building an exhaustive list, maybe we're done? 1325 if max > 0 && len(children) >= max { 1326 break 1327 } 1328 } 1329 return children, nil 1330 } 1331 1332 // InputIsID returns a bool if the user input for an image 1333 // is the image's partial or full id 1334 func (i *Image) InputIsID() bool { 1335 return strings.HasPrefix(i.ID(), i.InputName) 1336 } 1337 1338 // Containers a list of container IDs associated with the image 1339 func (i *Image) Containers() ([]string, error) { 1340 containers, err := i.imageruntime.store.Containers() 1341 if err != nil { 1342 return nil, err 1343 } 1344 var imageContainers []string 1345 for _, c := range containers { 1346 if c.ImageID == i.ID() { 1347 imageContainers = append(imageContainers, c.ID) 1348 } 1349 } 1350 return imageContainers, err 1351 } 1352 1353 // Comment returns the Comment for an image depending on its ManifestType 1354 func (i *Image) Comment(ctx context.Context, manifestType string) (string, error) { 1355 if manifestType == manifest.DockerV2Schema2MediaType { 1356 imgRef, err := i.toImageRef(ctx) 1357 if err != nil { 1358 return "", errors.Wrapf(err, "unable to create image reference from image") 1359 } 1360 blob, err := imgRef.ConfigBlob(ctx) 1361 if err != nil { 1362 return "", errors.Wrapf(err, "unable to get config blob from image") 1363 } 1364 b := manifest.Schema2Image{} 1365 if err := json.Unmarshal(blob, &b); err != nil { 1366 return "", err 1367 } 1368 return b.Comment, nil 1369 } 1370 ociv1Img, err := i.ociv1Image(ctx) 1371 if err != nil { 1372 if errors.Cause(err) == ErrImageIsBareList { 1373 return "", nil 1374 } 1375 return "", err 1376 } 1377 if len(ociv1Img.History) > 0 { 1378 return ociv1Img.History[0].Comment, nil 1379 } 1380 return "", nil 1381 } 1382 1383 // Save writes a container image to the filesystem 1384 func (i *Image) Save(ctx context.Context, source, format, output string, moreTags []string, quiet, compress bool) error { 1385 var ( 1386 writer io.Writer 1387 destRef types.ImageReference 1388 manifestType string 1389 err error 1390 ) 1391 1392 if quiet { 1393 writer = os.Stderr 1394 } 1395 switch format { 1396 case "oci-archive": 1397 destImageName := imageNameForSaveDestination(i, source) 1398 destRef, err = ociarchive.NewReference(output, destImageName) // destImageName may be "" 1399 if err != nil { 1400 return errors.Wrapf(err, "error getting OCI archive ImageReference for (%q, %q)", output, destImageName) 1401 } 1402 case "oci-dir": 1403 destRef, err = directory.NewReference(output) 1404 if err != nil { 1405 return errors.Wrapf(err, "error getting directory ImageReference for %q", output) 1406 } 1407 manifestType = imgspecv1.MediaTypeImageManifest 1408 case "docker-dir": 1409 destRef, err = directory.NewReference(output) 1410 if err != nil { 1411 return errors.Wrapf(err, "error getting directory ImageReference for %q", output) 1412 } 1413 manifestType = manifest.DockerV2Schema2MediaType 1414 case "docker-archive", "": 1415 destImageName := imageNameForSaveDestination(i, source) 1416 ref, err := dockerArchiveDstReference(destImageName) 1417 if err != nil { 1418 return err 1419 } 1420 destRef, err = dockerarchive.NewReference(output, ref) 1421 if err != nil { 1422 return errors.Wrapf(err, "error getting Docker archive ImageReference for %s:%v", output, ref) 1423 } 1424 default: 1425 return errors.Errorf("unknown format option %q", format) 1426 } 1427 // supports saving multiple tags to the same tar archive 1428 var additionaltags []reference.NamedTagged 1429 if len(moreTags) > 0 { 1430 additionaltags, err = GetAdditionalTags(moreTags) 1431 if err != nil { 1432 return err 1433 } 1434 } 1435 if err := i.PushImageToReference(ctx, destRef, manifestType, "", "", "", writer, compress, SigningOptions{}, &DockerRegistryOptions{}, additionaltags); err != nil { 1436 return errors.Wrapf(err, "unable to save %q", source) 1437 } 1438 i.newImageEvent(events.Save) 1439 return nil 1440 } 1441 1442 // dockerArchiveDestReference returns a NamedTagged reference for a tagged image and nil for untagged image. 1443 func dockerArchiveDstReference(normalizedInput string) (reference.NamedTagged, error) { 1444 if normalizedInput == "" { 1445 return nil, nil 1446 } 1447 ref, err := reference.ParseNormalizedNamed(normalizedInput) 1448 if err != nil { 1449 return nil, errors.Wrapf(err, "docker-archive parsing reference %s", normalizedInput) 1450 } 1451 ref = reference.TagNameOnly(ref) 1452 namedTagged, isTagged := ref.(reference.NamedTagged) 1453 if !isTagged { 1454 namedTagged = nil 1455 } 1456 return namedTagged, nil 1457 } 1458 1459 // GetConfigBlob returns a schema2image. If the image is not a schema2, then 1460 // it will return an error 1461 func (i *Image) GetConfigBlob(ctx context.Context) (*manifest.Schema2Image, error) { 1462 imageRef, err := i.toImageRef(ctx) 1463 if err != nil { 1464 return nil, err 1465 } 1466 b, err := imageRef.ConfigBlob(ctx) 1467 if err != nil { 1468 return nil, errors.Wrapf(err, "unable to get config blob for %s", i.ID()) 1469 } 1470 blob := manifest.Schema2Image{} 1471 if err := json.Unmarshal(b, &blob); err != nil { 1472 return nil, errors.Wrapf(err, "unable to parse image blob for %s", i.ID()) 1473 } 1474 return &blob, nil 1475 1476 } 1477 1478 // GetHealthCheck returns a HealthConfig for an image. This function only works with 1479 // schema2 images. 1480 func (i *Image) GetHealthCheck(ctx context.Context) (*manifest.Schema2HealthConfig, error) { 1481 configBlob, err := i.GetConfigBlob(ctx) 1482 if err != nil { 1483 return nil, err 1484 } 1485 return configBlob.ContainerConfig.Healthcheck, nil 1486 } 1487 1488 // newImageEvent creates a new event based on an image 1489 func (ir *Runtime) newImageEvent(status events.Status, name string) { 1490 e := events.NewEvent(status) 1491 e.Type = events.Image 1492 e.Name = name 1493 if err := ir.Eventer.Write(e); err != nil { 1494 logrus.Infof("unable to write event to %s", ir.EventsLogFilePath) 1495 } 1496 } 1497 1498 // newImageEvent creates a new event based on an image 1499 func (i *Image) newImageEvent(status events.Status) { 1500 e := events.NewEvent(status) 1501 e.ID = i.ID() 1502 e.Type = events.Image 1503 if len(i.Names()) > 0 { 1504 e.Name = i.Names()[0] 1505 } 1506 if err := i.imageruntime.Eventer.Write(e); err != nil { 1507 logrus.Infof("unable to write event to %s", i.imageruntime.EventsLogFilePath) 1508 } 1509 } 1510 1511 // LayerInfo keeps information of single layer 1512 type LayerInfo struct { 1513 // Layer ID 1514 ID string 1515 // Parent ID of current layer. 1516 ParentID string 1517 // ChildID of current layer. 1518 // there can be multiple children in case of fork 1519 ChildID []string 1520 // RepoTag will have image repo names, if layer is top layer of image 1521 RepoTags []string 1522 // Size stores Uncompressed size of layer. 1523 Size int64 1524 } 1525 1526 // GetLayersMapWithImageInfo returns map of image-layers, with associated information like RepoTags, parent and list of child layers. 1527 func GetLayersMapWithImageInfo(imageruntime *Runtime) (map[string]*LayerInfo, error) { 1528 1529 // Memory allocated to store map of layers with key LayerID. 1530 // Map will build dependency chain with ParentID and ChildID(s) 1531 layerInfoMap := make(map[string]*LayerInfo) 1532 1533 // scan all layers & fill size and parent id for each layer in layerInfoMap 1534 layers, err := imageruntime.store.Layers() 1535 if err != nil { 1536 return nil, err 1537 } 1538 for _, layer := range layers { 1539 _, ok := layerInfoMap[layer.ID] 1540 if !ok { 1541 layerInfoMap[layer.ID] = &LayerInfo{ 1542 ID: layer.ID, 1543 Size: layer.UncompressedSize, 1544 ParentID: layer.Parent, 1545 } 1546 } else { 1547 return nil, fmt.Errorf("detected multiple layers with the same ID %q", layer.ID) 1548 } 1549 } 1550 1551 // scan all layers & add all childid's for each layers to layerInfo 1552 for _, layer := range layers { 1553 _, ok := layerInfoMap[layer.ID] 1554 if ok { 1555 if layer.Parent != "" { 1556 layerInfoMap[layer.Parent].ChildID = append(layerInfoMap[layer.Parent].ChildID, layer.ID) 1557 } 1558 } else { 1559 return nil, fmt.Errorf("lookup error: layer-id %s, not found", layer.ID) 1560 } 1561 } 1562 1563 // Add the Repo Tags to Top layer of each image. 1564 imgs, err := imageruntime.store.Images() 1565 if err != nil { 1566 return nil, err 1567 } 1568 layerInfoMap[""] = &LayerInfo{} 1569 for _, img := range imgs { 1570 e, ok := layerInfoMap[img.TopLayer] 1571 if !ok { 1572 return nil, fmt.Errorf("top-layer for image %s not found local store", img.ID) 1573 } 1574 e.RepoTags = append(e.RepoTags, img.Names...) 1575 } 1576 return layerInfoMap, nil 1577 } 1578 1579 // BuildImageHierarchyMap stores hierarchy of images such that all parent layers using which image is built are stored in imageInfo 1580 // Layers are added such that (Start)RootLayer->...intermediate Parent Layer(s)-> TopLayer(End) 1581 func BuildImageHierarchyMap(imageInfo *InfoImage, layerMap map[string]*LayerInfo, layerID string) error { 1582 if layerID == "" { 1583 return nil 1584 } 1585 ll, ok := layerMap[layerID] 1586 if !ok { 1587 return fmt.Errorf("lookup error: layerid %s not found", layerID) 1588 } 1589 if err := BuildImageHierarchyMap(imageInfo, layerMap, ll.ParentID); err != nil { 1590 return err 1591 } 1592 1593 imageInfo.Layers = append(imageInfo.Layers, *ll) 1594 return nil 1595 }