image_tag.go 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. package containerd
  2. import (
  3. "context"
  4. cerrdefs "github.com/containerd/containerd/errdefs"
  5. containerdimages "github.com/containerd/containerd/images"
  6. "github.com/containerd/containerd/log"
  7. "github.com/docker/distribution/reference"
  8. "github.com/docker/docker/errdefs"
  9. "github.com/docker/docker/image"
  10. "github.com/pkg/errors"
  11. "github.com/sirupsen/logrus"
  12. )
  13. // TagImage creates an image named as newTag and targeting the given descriptor id.
  14. func (i *ImageService) TagImage(ctx context.Context, imageID image.ID, newTag reference.Named) error {
  15. target, err := i.resolveDescriptor(ctx, imageID.String())
  16. if err != nil {
  17. return errors.Wrapf(err, "failed to resolve image id %q to a descriptor", imageID.String())
  18. }
  19. newImg := containerdimages.Image{
  20. Name: newTag.String(),
  21. Target: target,
  22. }
  23. is := i.client.ImageService()
  24. _, err = is.Create(ctx, newImg)
  25. if err != nil {
  26. if !cerrdefs.IsAlreadyExists(err) {
  27. return errdefs.System(errors.Wrapf(err, "failed to create image with name %s and target %s", newImg.Name, newImg.Target.Digest.String()))
  28. }
  29. replacedImg, err := is.Get(ctx, newImg.Name)
  30. if err != nil {
  31. return errdefs.Unknown(errors.Wrapf(err, "creating image %s failed because it already exists, but accessing it also failed", newImg.Name))
  32. }
  33. // Check if image we would replace already resolves to the same target.
  34. // No need to do anything.
  35. if replacedImg.Target.Digest == target.Digest {
  36. i.LogImageEvent(imageID.String(), reference.FamiliarString(newTag), "tag")
  37. return nil
  38. }
  39. // If there already exists an image with this tag, delete it
  40. if err := i.softImageDelete(ctx, replacedImg); err != nil {
  41. return errors.Wrapf(err, "failed to delete previous image %s", replacedImg.Name)
  42. }
  43. if _, err = is.Create(context.Background(), newImg); err != nil {
  44. return errdefs.System(errors.Wrapf(err, "failed to create an image %s with target %s after deleting the existing one",
  45. newImg.Name, imageID.String()))
  46. }
  47. }
  48. logger := log.G(ctx).WithFields(logrus.Fields{
  49. "imageID": imageID.String(),
  50. "tag": newTag.String(),
  51. })
  52. logger.Info("image created")
  53. defer i.LogImageEvent(imageID.String(), reference.FamiliarString(newTag), "tag")
  54. // The tag succeeded, check if the source image is dangling
  55. sourceDanglingImg, err := is.Get(context.Background(), danglingImageName(target.Digest))
  56. if err != nil {
  57. if !cerrdefs.IsNotFound(err) {
  58. logger.WithError(err).Warn("unexpected error when checking if source image is dangling")
  59. }
  60. return nil
  61. }
  62. // Delete the source dangling image, as it's no longer dangling.
  63. if err := is.Delete(context.Background(), sourceDanglingImg.Name); err != nil {
  64. logger.WithError(err).Warn("unexpected error when deleting dangling image")
  65. }
  66. return nil
  67. }