zotregistry.dev/zot@v1.4.4-0.20240314164342-eec277e14d20/pkg/extensions/sync/references/referrers_tag.go (about)

     1  //go:build sync
     2  // +build sync
     3  
     4  package references
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"net/http"
    11  	"strings"
    12  
    13  	godigest "github.com/opencontainers/go-digest"
    14  	ispec "github.com/opencontainers/image-spec/specs-go/v1"
    15  
    16  	zerr "zotregistry.dev/zot/errors"
    17  	"zotregistry.dev/zot/pkg/common"
    18  	"zotregistry.dev/zot/pkg/extensions/sync/constants"
    19  	client "zotregistry.dev/zot/pkg/extensions/sync/httpclient"
    20  	"zotregistry.dev/zot/pkg/log"
    21  	"zotregistry.dev/zot/pkg/meta"
    22  	mTypes "zotregistry.dev/zot/pkg/meta/types"
    23  	"zotregistry.dev/zot/pkg/storage"
    24  )
    25  
    26  type TagReferences struct {
    27  	client          *client.Client
    28  	storeController storage.StoreController
    29  	metaDB          mTypes.MetaDB
    30  	log             log.Logger
    31  }
    32  
    33  func NewTagReferences(httpClient *client.Client, storeController storage.StoreController,
    34  	metaDB mTypes.MetaDB, log log.Logger,
    35  ) TagReferences {
    36  	return TagReferences{
    37  		client:          httpClient,
    38  		storeController: storeController,
    39  		metaDB:          metaDB,
    40  		log:             log,
    41  	}
    42  }
    43  
    44  func (ref TagReferences) Name() string {
    45  	return constants.Tag
    46  }
    47  
    48  func (ref TagReferences) IsSigned(ctx context.Context, remoteRepo, subjectDigestStr string) bool {
    49  	return false
    50  }
    51  
    52  func (ref TagReferences) canSkipReferences(localRepo, subjectDigestStr, digest string) (bool, error) {
    53  	imageStore := ref.storeController.GetImageStore(localRepo)
    54  
    55  	_, localDigest, _, err := imageStore.GetImageManifest(localRepo, getReferrersTagFromSubjectDigest(subjectDigestStr))
    56  	if err != nil {
    57  		if errors.Is(err, zerr.ErrManifestNotFound) {
    58  			return false, nil
    59  		}
    60  
    61  		ref.log.Error().Str("errorType", common.TypeOf(err)).
    62  			Str("repository", localRepo).Str("subject", subjectDigestStr).
    63  			Err(err).Msg("couldn't get local index with referrers tag for image")
    64  
    65  		return false, err
    66  	}
    67  
    68  	if localDigest.String() != digest {
    69  		return false, nil
    70  	}
    71  
    72  	ref.log.Info().Str("repository", localRepo).Str("reference", subjectDigestStr).
    73  		Msg("skipping index with referrers tag for image, already synced")
    74  
    75  	return true, nil
    76  }
    77  
    78  func (ref TagReferences) SyncReferences(ctx context.Context, localRepo, remoteRepo, subjectDigestStr string) (
    79  	[]godigest.Digest, error,
    80  ) {
    81  	refsDigests := make([]godigest.Digest, 0, 10)
    82  
    83  	index, indexContent, err := ref.getIndex(ctx, remoteRepo, subjectDigestStr)
    84  	if err != nil {
    85  		return refsDigests, err
    86  	}
    87  
    88  	skipTagRefs, err := ref.canSkipReferences(localRepo, subjectDigestStr, string(godigest.FromBytes(indexContent)))
    89  	if err != nil {
    90  		ref.log.Error().Err(err).Str("repository", localRepo).Str("subject", subjectDigestStr).
    91  			Msg("couldn't check if the upstream index with referrers tag for image can be skipped")
    92  	}
    93  
    94  	if skipTagRefs {
    95  		return refsDigests, nil
    96  	}
    97  
    98  	imageStore := ref.storeController.GetImageStore(localRepo)
    99  
   100  	ref.log.Info().Str("repository", localRepo).Str("subject", subjectDigestStr).
   101  		Msg("syncing oci references for image")
   102  
   103  	for _, referrer := range index.Manifests {
   104  		referenceBuf, referenceDigest, err := syncManifest(ctx, ref.client, imageStore, localRepo, remoteRepo,
   105  			referrer, subjectDigestStr, ref.log)
   106  		if err != nil {
   107  			return refsDigests, err
   108  		}
   109  
   110  		refsDigests = append(refsDigests, referenceDigest)
   111  
   112  		if ref.metaDB != nil {
   113  			ref.log.Debug().Str("repository", localRepo).Str("subject", subjectDigestStr).Str("component", "metadb").
   114  				Msg("trying to add oci references for image")
   115  
   116  			err = meta.SetImageMetaFromInput(ctx, localRepo, referenceDigest.String(), referrer.MediaType,
   117  				referenceDigest, referenceBuf, ref.storeController.GetImageStore(localRepo),
   118  				ref.metaDB, ref.log)
   119  			if err != nil {
   120  				return refsDigests, fmt.Errorf("failed to set metadata for oci reference in '%s@%s': %w",
   121  					localRepo, subjectDigestStr, err)
   122  			}
   123  
   124  			ref.log.Info().Str("repository", localRepo).Str("subject", subjectDigestStr).Str("component", "metadb").
   125  				Msg("successfully added oci references to MetaDB for image")
   126  		}
   127  	}
   128  
   129  	ref.log.Info().Str("repository", localRepo).Str("subject", subjectDigestStr).
   130  		Msg("syncing index with referrers tag for image")
   131  
   132  	referrersTag := getReferrersTagFromSubjectDigest(subjectDigestStr)
   133  
   134  	_, _, err = imageStore.PutImageManifest(localRepo, referrersTag, index.MediaType, indexContent)
   135  	if err != nil {
   136  		ref.log.Error().Str("errorType", common.TypeOf(err)).
   137  			Str("repository", localRepo).Str("subject", subjectDigestStr).
   138  			Err(err).Msg("couldn't upload index with referrers tag for image")
   139  
   140  		return refsDigests, err
   141  	}
   142  
   143  	ref.log.Info().Str("repository", localRepo).Str("subject", subjectDigestStr).
   144  		Msg("successfully synced index with referrers tag for image")
   145  
   146  	return refsDigests, nil
   147  }
   148  
   149  func (ref TagReferences) getIndex(
   150  	ctx context.Context, repo, subjectDigestStr string,
   151  ) (ispec.Index, []byte, error) {
   152  	var index ispec.Index
   153  
   154  	content, _, statusCode, err := ref.client.MakeGetRequest(ctx, &index, ispec.MediaTypeImageIndex,
   155  		"v2", repo, "manifests", getReferrersTagFromSubjectDigest(subjectDigestStr))
   156  	if err != nil {
   157  		if statusCode == http.StatusNotFound {
   158  			ref.log.Debug().Str("repository", repo).Str("subject", subjectDigestStr).
   159  				Msg("couldn't find any index with referrers tag for image, skipping")
   160  
   161  			return index, []byte{}, zerr.ErrSyncReferrerNotFound
   162  		}
   163  
   164  		return index, []byte{}, err
   165  	}
   166  
   167  	return index, content, nil
   168  }
   169  
   170  func getReferrersTagFromSubjectDigest(digestStr string) string {
   171  	return strings.Replace(digestStr, ":", "-", 1)
   172  }