Repository: moby/moby
File: daemon/containerd/image_tag.go (Go)

Code before:

	return i.createOrReplaceImage(ctx, newImg)
}

// createOrReplaceImage creates a new image with the given name and target descriptor.
// If an image with the same name already exists, it will be replaced.
// Overwritten image will be persisted as a dangling image if it's a last
// reference to that image.
func (i *ImageService) createOrReplaceImage(ctx context.Context, newImg containerdimages.Image) error {
	_, 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 == newImg.Target.Digest {
			i.LogImageEvent(newImg.Name, imageFamiliarName(newImg), 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, newImg.Target.Digest))
		}
	}

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

	defer i.LogImageEvent(newImg.Name, imageFamiliarName(newImg), events.ActionTag)

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

Review comment (suggestion):
This is changing `id` to `name`, correct? Mostly considering here that name could be easier to read, but `ID` may work also in case where there's no name associated. I recall we had some similar discussion around "network" events (I think) at some point, where we could not add a name to all of the related events, which made it more difficult to correlate events.

Code after:

	return i.createOrReplaceImage(ctx, newImg)
}

// createOrReplaceImage creates a new image with the given name and target descriptor.
// If an image with the same name already exists, it will be replaced.
// Overwritten image will be persisted as a dangling image if it's a last
// reference to that image.
func (i *ImageService) createOrReplaceImage(ctx context.Context, newImg containerdimages.Image) error {
	// Delete the source dangling image, as it's no longer dangling.
	// Unless, the image to be created itself is dangling.
	danglingName := danglingImageName(newImg.Target.Digest)

	// The created image is a dangling image.
	creatingDangling := newImg.Name == danglingName

	if _, err := i.images.Create(ctx, newImg); 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 == newImg.Target.Digest {
			if !creatingDangling {
				i.LogImageEvent(replacedImg.Target.Digest.String(), imageFamiliarName(newImg), 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, newImg.Target.Digest))
		}
	}

	logger := log.G(ctx).WithFields(log.Fields{
		"imageID": newImg.Target.Digest,
		"tag":     newImg.Name,