0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2025-01-06 22:40:28 -05:00
zot/pkg/extensions/sync/references/references.go
LaurentiuNiculae 28de980319
feat(refator): refactoring repodb into meta (#1626)
Signed-off-by: Laurentiu Niculae <niculae.laurentiu1@gmail.com>
2023-07-18 10:27:26 -07:00

213 lines
5.8 KiB
Go

//go:build sync
// +build sync
package references
import (
"bytes"
"fmt"
"net/http"
notreg "github.com/notaryproject/notation-go/registry"
godigest "github.com/opencontainers/go-digest"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
artifactspec "github.com/oras-project/artifacts-spec/specs-go/v1"
"github.com/sigstore/cosign/v2/pkg/oci/static"
"zotregistry.io/zot/pkg/common"
client "zotregistry.io/zot/pkg/extensions/sync/httpclient"
"zotregistry.io/zot/pkg/log"
mTypes "zotregistry.io/zot/pkg/meta/types"
"zotregistry.io/zot/pkg/storage"
storageTypes "zotregistry.io/zot/pkg/storage/types"
)
type Reference interface {
// Returns name of reference (OCIReference/CosignReference/OrasReference)
Name() string
// Returns whether or not image is signed
IsSigned(upstreamRepo, subjectDigestStr string) bool
// Sync recursively all references for a subject digest (can be image/artifacts/signatures)
SyncReferences(localRepo, upstreamRepo, subjectDigestStr string) ([]godigest.Digest, error)
}
type References struct {
referenceList []Reference
log log.Logger
}
func NewReferences(httpClient *client.Client, storeController storage.StoreController,
metaDB mTypes.MetaDB, log log.Logger,
) References {
refs := References{log: log}
refs.referenceList = append(refs.referenceList, NewCosignReference(httpClient, storeController, metaDB, log))
refs.referenceList = append(refs.referenceList, NewOciReferences(httpClient, storeController, metaDB, log))
refs.referenceList = append(refs.referenceList, NewORASReferences(httpClient, storeController, metaDB, log))
return refs
}
func (refs References) IsSigned(upstreamRepo, subjectDigestStr string) bool {
for _, ref := range refs.referenceList {
ok := ref.IsSigned(upstreamRepo, subjectDigestStr)
if ok {
return true
}
}
return false
}
func (refs References) SyncAll(localRepo, upstreamRepo, subjectDigestStr string) error {
seen := &[]godigest.Digest{}
return refs.syncAll(localRepo, upstreamRepo, subjectDigestStr, seen)
}
func (refs References) syncAll(localRepo, upstreamRepo, subjectDigestStr string, seen *[]godigest.Digest) error {
var err error
var syncedRefsDigests []godigest.Digest
// mark subject digest as seen as soon as it comes in
*seen = append(*seen, godigest.Digest(subjectDigestStr))
// for each reference type(cosign/oci/oras reference)
for _, ref := range refs.referenceList {
syncedRefsDigests, err = ref.SyncReferences(localRepo, upstreamRepo, subjectDigestStr)
if err != nil {
refs.log.Debug().Err(err).
Str("reference type", ref.Name()).
Str("image", fmt.Sprintf("%s:%s", upstreamRepo, subjectDigestStr)).
Msg("couldn't sync image referrer")
}
// for each synced references
for _, refDigest := range syncedRefsDigests {
if !common.Contains(*seen, refDigest) {
// sync all references pointing to this one
err = refs.syncAll(localRepo, upstreamRepo, refDigest.String(), seen)
}
}
}
return err
}
func (refs References) SyncReference(localRepo, upstreamRepo, subjectDigestStr, referenceType string) error {
var err error
var syncedRefsDigests []godigest.Digest
for _, ref := range refs.referenceList {
if ref.Name() == referenceType {
syncedRefsDigests, err = ref.SyncReferences(localRepo, upstreamRepo, subjectDigestStr)
if err != nil {
refs.log.Error().Err(err).
Str("reference type", ref.Name()).
Str("image", fmt.Sprintf("%s:%s", upstreamRepo, subjectDigestStr)).
Msg("couldn't sync image referrer")
return err
}
for _, refDigest := range syncedRefsDigests {
err = refs.SyncAll(localRepo, upstreamRepo, refDigest.String())
}
}
}
return err
}
func syncBlob(client *client.Client, imageStore storageTypes.ImageStore, localRepo, remoteRepo string,
digest godigest.Digest, log log.Logger,
) error {
var resultPtr interface{}
body, _, statusCode, err := client.MakeGetRequest(resultPtr, "", "v2", remoteRepo, "blobs", digest.String())
if err != nil {
if statusCode != http.StatusOK {
log.Info().Str("repo", remoteRepo).Str("digest", digest.String()).Msg("couldn't get remote blob")
return err
}
}
_, _, err = imageStore.FullBlobUpload(localRepo, bytes.NewBuffer(body), digest)
if err != nil {
log.Error().Str("errorType", common.TypeOf(err)).Str("digest", digest.String()).Str("repo", localRepo).
Err(err).Msg("couldn't upload blob")
return err
}
return nil
}
func manifestsEqual(manifest1, manifest2 ispec.Manifest) bool {
if manifest1.Config.Digest == manifest2.Config.Digest &&
manifest1.Config.MediaType == manifest2.Config.MediaType &&
manifest1.Config.Size == manifest2.Config.Size {
if descriptorsEqual(manifest1.Layers, manifest2.Layers) {
return true
}
}
return false
}
func artifactDescriptorsEqual(desc1, desc2 []artifactspec.Descriptor) bool {
if len(desc1) != len(desc2) {
return false
}
for id, desc := range desc1 {
if desc.Digest != desc2[id].Digest ||
desc.Size != desc2[id].Size ||
desc.MediaType != desc2[id].MediaType ||
desc.ArtifactType != desc2[id].ArtifactType {
return false
}
}
return true
}
func descriptorsEqual(desc1, desc2 []ispec.Descriptor) bool {
if len(desc1) != len(desc2) {
return false
}
for id, desc := range desc1 {
if !descriptorEqual(desc, desc2[id]) {
return false
}
}
return true
}
func descriptorEqual(desc1, desc2 ispec.Descriptor) bool {
if desc1.Size == desc2.Size &&
desc1.Digest == desc2.Digest &&
desc1.MediaType == desc2.MediaType &&
desc1.Annotations[static.SignatureAnnotationKey] == desc2.Annotations[static.SignatureAnnotationKey] {
return true
}
return false
}
func getNotationManifestsFromOCIRefs(ociRefs ispec.Index) []ispec.Descriptor {
notaryManifests := []ispec.Descriptor{}
for _, ref := range ociRefs.Manifests {
if ref.ArtifactType == notreg.ArtifactTypeNotation {
notaryManifests = append(notaryManifests, ref)
}
}
return notaryManifests
}