dotcloud/docker

View on GitHub
daemon/containerd/image_tag.go

Summary

Maintainability
A
50 mins
Test Coverage
package containerd

import (
    "context"
    "fmt"

    containerdimages "github.com/containerd/containerd/images"
    cerrdefs "github.com/containerd/errdefs"
    "github.com/containerd/log"
    "github.com/distribution/reference"
    "github.com/docker/docker/api/types/events"
    "github.com/docker/docker/errdefs"
    "github.com/docker/docker/image"
    "github.com/pkg/errors"
)

// TagImage creates an image named as newTag and targeting the given descriptor id.
func (i *ImageService) TagImage(ctx context.Context, imageID image.ID, newTag reference.Named) error {
    targetImage, err := i.resolveImage(ctx, imageID.String())
    if err != nil {
        return errors.Wrapf(err, "failed to resolve image id %q to a descriptor", imageID.String())
    }

    newImg := containerdimages.Image{
        Name:   newTag.String(),
        Target: targetImage.Target,
        Labels: targetImage.Labels,
    }

    _, err = i.images.Create(ctx, newImg)
    if err != nil {
        if !cerrdefs.IsAlreadyExists(err) {
            return errdefs.System(errors.Wrapf(err, "failed to create image with name %s and target %s", newImg.Name, newImg.Target.Digest.String()))
        }

        replacedImg, all, err := i.resolveAllReferences(ctx, newImg.Name)
        if err != nil {
            return errdefs.Unknown(errors.Wrapf(err, "creating image %s failed because it already exists, but accessing it also failed", newImg.Name))
        } else if replacedImg == nil {
            return errdefs.Unknown(fmt.Errorf("creating image %s failed because it already exists, but failed to resolve", newImg.Name))
        }

        // Check if image we would replace already resolves to the same target.
        // No need to do anything.
        if replacedImg.Target.Digest == targetImage.Target.Digest {
            i.LogImageEvent(imageID.String(), reference.FamiliarString(newTag), events.ActionTag)
            return nil
        }

        // If there already exists an image with this tag, delete it
        if err := i.softImageDelete(ctx, *replacedImg, all); err != nil {
            return errors.Wrapf(err, "failed to delete previous image %s", replacedImg.Name)
        }

        if _, err = i.images.Create(context.WithoutCancel(ctx), newImg); err != nil {
            return errdefs.System(errors.Wrapf(err, "failed to create an image %s with target %s after deleting the existing one",
                newImg.Name, imageID.String()))
        }
    }

    logger := log.G(ctx).WithFields(log.Fields{
        "imageID": imageID.String(),
        "tag":     newTag.String(),
    })
    logger.Info("image created")

    defer i.LogImageEvent(imageID.String(), reference.FamiliarString(newTag), events.ActionTag)

    // Delete the source dangling image, as it's no longer dangling.
    if err := i.images.Delete(context.WithoutCancel(ctx), danglingImageName(targetImage.Target.Digest)); err != nil {
        logger.WithError(err).Warn("unexpected error when deleting dangling image")
    }

    return nil
}