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