Skip to content

Commit

Permalink
fix(sync): fixed way of updating repodb when syncing a signature
Browse files Browse the repository at this point in the history
Signed-off-by: Laurentiu Niculae <niculae.laurentiu1@gmail.com>
  • Loading branch information
laurentiuNiculae committed May 12, 2023
1 parent d17fe00 commit 45f2c81
Show file tree
Hide file tree
Showing 14 changed files with 284 additions and 272 deletions.
1 change: 0 additions & 1 deletion errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ var (
ErrTagMetaNotFound = errors.New("repodb: tag metadata not found for given repo and tag names")
ErrTypeAssertionFailed = errors.New("storage: failed DatabaseDriver type assertion")
ErrInvalidRequestParams = errors.New("resolver: parameter sent has invalid value")
ErrOrphanSignature = errors.New("repodb: signature detected but signed image doesn't exit")
ErrBadCtxFormat = errors.New("type assertion failed")
ErrEmptyRepoName = errors.New("repodb: repo name can't be empty string")
ErrEmptyTag = errors.New("repodb: tag can't be empty string")
Expand Down
13 changes: 3 additions & 10 deletions pkg/api/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,10 +434,7 @@ func (rh *RouteHandler) GetManifest(response http.ResponseWriter, request *http.

if rh.c.RepoDB != nil {
err := meta.OnGetManifest(name, reference, content, rh.c.StoreController, rh.c.RepoDB, rh.c.Log)

if errors.Is(err, zerr.ErrOrphanSignature) {
rh.c.Log.Error().Err(err).Msg("image is an orphan signature")
} else if err != nil {
if err != nil {
response.WriteHeader(http.StatusInternalServerError)

return
Expand Down Expand Up @@ -647,9 +644,7 @@ func (rh *RouteHandler) UpdateManifest(response http.ResponseWriter, request *ht
if rh.c.RepoDB != nil {
err := meta.OnUpdateManifest(name, reference, mediaType, digest, body, rh.c.StoreController, rh.c.RepoDB,
rh.c.Log)
if errors.Is(err, zerr.ErrOrphanSignature) {
rh.c.Log.Error().Err(err).Msg("pushed image is an orphan signature")
} else if err != nil {
if err != nil {
response.WriteHeader(http.StatusInternalServerError)

return
Expand Down Expand Up @@ -746,9 +741,7 @@ func (rh *RouteHandler) DeleteManifest(response http.ResponseWriter, request *ht
if rh.c.RepoDB != nil {
err := meta.OnDeleteManifest(name, reference, mediaType, manifestDigest, manifestBlob,
rh.c.StoreController, rh.c.RepoDB, rh.c.Log)
if errors.Is(err, zerr.ErrOrphanSignature) {
rh.c.Log.Error().Err(err).Msg("pushed image is an orphan signature")
} else if err != nil {
if err != nil {
response.WriteHeader(http.StatusInternalServerError)

return
Expand Down
36 changes: 26 additions & 10 deletions pkg/extensions/sync/signatures.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ func (sig *signaturesCopier) syncCosignSignature(localRepo, remoteRepo, digestSt
}

// push manifest
_, err = imageStore.PutImageManifest(localRepo, cosignTag,
signatureDigest, err := imageStore.PutImageManifest(localRepo, cosignTag,
ispec.MediaTypeImageManifest, cosignManifestBuf)
if err != nil {
sig.log.Error().Str("errorType", common.TypeOf(err)).
Expand All @@ -193,9 +193,10 @@ func (sig *signaturesCopier) syncCosignSignature(localRepo, remoteRepo, digestSt
sig.log.Debug().Str("repository", localRepo).Str("digest", digestStr).
Msg("trying to sync cosign signature for repo digest")

err = repodb.SetMetadataFromInput(localRepo, cosignTag, ispec.MediaTypeImageManifest,
godigest.FromBytes(cosignManifestBuf), cosignManifestBuf, sig.storeController.GetImageStore(localRepo),
sig.repoDB, sig.log)
err := sig.repoDB.AddManifestSignature(localRepo, godigest.Digest(digestStr), repodb.SignatureMetadata{
SignatureType: repodb.CosignType,
SignatureDigest: signatureDigest.String(),
})
if err != nil {
return fmt.Errorf("failed to set metadata for cosign signature '%s@%s': %w", localRepo, digestStr, err)
}
Expand Down Expand Up @@ -258,7 +259,7 @@ func (sig *signaturesCopier) syncORASRefs(localRepo, remoteRepo, digestStr strin
}
}

_, err = imageStore.PutImageManifest(localRepo, ref.Digest.String(),
signatureDigest, err := imageStore.PutImageManifest(localRepo, ref.Digest.String(),
oras.MediaTypeArtifactManifest, body)
if err != nil {
sig.log.Error().Str("errorType", common.TypeOf(err)).
Expand All @@ -272,8 +273,10 @@ func (sig *signaturesCopier) syncORASRefs(localRepo, remoteRepo, digestStr strin
sig.log.Debug().Str("repository", localRepo).Str("digest", digestStr).
Msg("trying to sync oras artifact for digest")

err = repodb.SetMetadataFromInput(localRepo, ref.Digest.String(), ref.MediaType,
ref.Digest, body, sig.storeController.GetImageStore(localRepo), sig.repoDB, sig.log)
err := sig.repoDB.AddManifestSignature(localRepo, godigest.Digest(digestStr), repodb.SignatureMetadata{
SignatureType: repodb.NotationType,
SignatureDigest: signatureDigest.String(),
})
if err != nil {
return fmt.Errorf("failed to set metadata for oras artifact '%s@%s': %w", localRepo, digestStr, err)
}
Expand Down Expand Up @@ -371,9 +374,22 @@ func (sig *signaturesCopier) syncOCIRefs(localRepo, remoteRepo, digestStr string
if sig.repoDB != nil {
sig.log.Debug().Str("repository", localRepo).Str("digest", digestStr).Msg("trying to add OCI refs for repo digest")

err = repodb.SetMetadataFromInput(localRepo, digestStr, ref.MediaType,
digest, OCIRefBody, sig.storeController.GetImageStore(localRepo),
sig.repoDB, sig.log)
isSig, _, signedManifestDig, err := storage.CheckIsImageSignature(localRepo, OCIRefBody, ref.Digest.String())
if err != nil {
return fmt.Errorf("failed to set metadata for OCI ref in '%s@%s': %w", localRepo, digestStr, err)
}

if isSig {
err = sig.repoDB.AddManifestSignature(localRepo, signedManifestDig, repodb.SignatureMetadata{
SignatureType: repodb.NotationType,
SignatureDigest: digestStr,
})
} else {
err = repodb.SetImageMetaFromInput(localRepo, digestStr, ref.MediaType,
digest, OCIRefBody, sig.storeController.GetImageStore(localRepo),
sig.repoDB, sig.log)
}

if err != nil {
return fmt.Errorf("failed to set metadata for OCI ref in '%s@%s': %w", localRepo, digestStr, err)
}
Expand Down
92 changes: 92 additions & 0 deletions pkg/extensions/sync/sync_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (
syncconf "zotregistry.io/zot/pkg/extensions/config/sync"
"zotregistry.io/zot/pkg/extensions/sync"
logger "zotregistry.io/zot/pkg/log"
"zotregistry.io/zot/pkg/meta/repodb"
"zotregistry.io/zot/pkg/storage"
"zotregistry.io/zot/pkg/storage/local"
"zotregistry.io/zot/pkg/test"
Expand Down Expand Up @@ -3637,6 +3638,97 @@ func TestSignatures(t *testing.T) {
})
}

func getPortFromBaseURL(baseURL string) string {
slice := strings.Split(baseURL, ":")

return slice[len(slice)-1]
}

func TestSyncedSignaturesRepoDB(t *testing.T) {
Convey("Verify that repodb update correctly when syncing a signature", t, func() {
repoName := "signed-repo"
tag := "random-signed-image"
updateDuration := 30 * time.Minute

// Create source registry

sctlr, srcBaseURL, srcDir, _, _ := makeUpstreamServer(t, false, false)
t.Log(srcDir)
srcPort := getPortFromBaseURL(srcBaseURL)

scm := test.NewControllerManager(sctlr)
scm.StartAndWait(sctlr.Config.HTTP.Port)
defer scm.StopServer()

// Push an image
destImage, err := test.GetRandomImage(tag)
So(err, ShouldBeNil)

signedImageDigest, err := destImage.Digest()
So(err, ShouldBeNil)

err = test.UploadImage(destImage, srcBaseURL, repoName)
So(err, ShouldBeNil)

err = test.SignImageUsingNotary(repoName+":"+tag, srcPort)
So(err, ShouldBeNil)

err = test.SignImageUsingCosign(repoName+":"+tag, srcPort)
So(err, ShouldBeNil)

// Create destination registry
var (
regex = ".*"
semver = false
tlsVerify = false
defaultVal = true
)

syncConfig := &syncconf.Config{
Enable: &defaultVal,
Registries: []syncconf.RegistryConfig{
{
Content: []syncconf.Content{
{
Prefix: repoName,
Tags: &syncconf.Tags{Regex: &regex, Semver: &semver},
},
},
URLs: []string{srcBaseURL},
PollInterval: updateDuration,
TLSVerify: &tlsVerify,
CertDir: "",
OnDemand: true,
},
},
}

dctlr, destBaseURL, dstDir, _ := makeDownstreamServer(t, false, syncConfig)
t.Log(dstDir)

dcm := test.NewControllerManager(dctlr)
dcm.StartAndWait(dctlr.Config.HTTP.Port)
defer dcm.StopServer()

// Trigger SyncOnDemand
resp, err := resty.R().Get(destBaseURL + "/v2/" + repoName + "/manifests/" + tag)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusOK)

repoMeta, err := dctlr.RepoDB.GetRepoMeta(repoName)
So(err, ShouldBeNil)
So(repoMeta.Tags, ShouldContainKey, tag)
So(len(repoMeta.Tags), ShouldEqual, 1)
So(repoMeta.Signatures, ShouldContainKey, signedImageDigest.String())

imageSignatures := repoMeta.Signatures[signedImageDigest.String()]
So(imageSignatures, ShouldContainKey, repodb.CosignType)
So(len(imageSignatures[repodb.CosignType]), ShouldEqual, 1)
So(imageSignatures, ShouldContainKey, repodb.NotationType)
So(len(imageSignatures[repodb.NotationType]), ShouldEqual, 1)
})
}

func TestOnDemandRetryGoroutine(t *testing.T) {
Convey("Verify ondemand sync retries in background on error", t, func() {
srcPort := test.GetFreePort()
Expand Down
4 changes: 2 additions & 2 deletions pkg/extensions/sync/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ func pushSyncedLocalImage(localRepo, reference, localCachePath string,
}

if repoDB != nil {
err = repodb.SetMetadataFromInput(localRepo, reference, mediaType,
err = repodb.SetImageMetaFromInput(localRepo, reference, mediaType,
manifestDigest, manifestBlob, imageStore, repoDB, log)
if err != nil {
return fmt.Errorf("failed to set metadata for image '%s %s': %w", localRepo, reference, err)
Expand Down Expand Up @@ -403,7 +403,7 @@ func copyManifest(localRepo string, manifestContent []byte, reference string, re
}

if repoDB != nil {
err = repodb.SetMetadataFromInput(localRepo, reference, ispec.MediaTypeImageManifest,
err = repodb.SetImageMetaFromInput(localRepo, reference, ispec.MediaTypeImageManifest,
digest, manifestContent, imageStore, repoDB, log)
if err != nil {
log.Error().Str("errorType", common.TypeOf(err)).
Expand Down
88 changes: 46 additions & 42 deletions pkg/meta/repodb/boltdb-wrapper/boltdb_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,14 +265,10 @@ func (bdw DBWrapper) SetReferrer(repo string, referredDigest godigest.Digest, re

// create a new object
repoMeta := repodb.RepoMetadata{
Name: repo,
Tags: map[string]repodb.Descriptor{},
Statistics: map[string]repodb.DescriptorStatistics{
referredDigest.String(): {},
},
Signatures: map[string]repodb.ManifestSignatures{
referredDigest.String(): {},
},
Name: repo,
Tags: map[string]repodb.Descriptor{},
Statistics: map[string]repodb.DescriptorStatistics{},
Signatures: map[string]repodb.ManifestSignatures{},
Referrers: map[string][]repodb.ReferrerInfo{
referredDigest.String(): {
referrer,
Expand Down Expand Up @@ -404,32 +400,22 @@ func (bdw *DBWrapper) SetRepoReference(repo string, reference string, manifestDi

repoMetaBlob := buck.Get([]byte(repo))

// object not found
if len(repoMetaBlob) == 0 {
var err error
// create a new object
repoMeta := repodb.RepoMetadata{
Name: repo,
Tags: map[string]repodb.Descriptor{},
Statistics: map[string]repodb.DescriptorStatistics{},
Signatures: map[string]repodb.ManifestSignatures{},
Referrers: map[string][]repodb.ReferrerInfo{},
}
repoMeta := repodb.RepoMetadata{
Name: repo,
Tags: map[string]repodb.Descriptor{},
Statistics: map[string]repodb.DescriptorStatistics{},
Signatures: map[string]repodb.ManifestSignatures{},
Referrers: map[string][]repodb.ReferrerInfo{},
}

repoMetaBlob, err = json.Marshal(repoMeta)
// object not found
if len(repoMetaBlob) > 0 {
err := json.Unmarshal(repoMetaBlob, &repoMeta)
if err != nil {
return err
}
}

// object found
var repoMeta repodb.RepoMetadata

err := json.Unmarshal(repoMetaBlob, &repoMeta)
if err != nil {
return err
}

if !common.ReferenceIsDigest(reference) {
repoMeta.Tags[reference] = repodb.Descriptor{
Digest: manifestDigest.String(),
Expand All @@ -449,7 +435,7 @@ func (bdw *DBWrapper) SetRepoReference(repo string, reference string, manifestDi
repoMeta.Referrers[manifestDigest.String()] = []repodb.ReferrerInfo{}
}

repoMetaBlob, err = json.Marshal(repoMeta)
repoMetaBlob, err := json.Marshal(repoMeta)
if err != nil {
return err
}
Expand Down Expand Up @@ -747,8 +733,33 @@ func (bdw *DBWrapper) AddManifestSignature(repo string, signedManifestDigest god
buck := tx.Bucket([]byte(bolt.RepoMetadataBucket))

repoMetaBlob := buck.Get([]byte(repo))
if repoMetaBlob == nil {
return zerr.ErrManifestMetaNotFound

if len(repoMetaBlob) == 0 {
var err error
// create a new object
repoMeta := repodb.RepoMetadata{
Name: repo,
Tags: map[string]repodb.Descriptor{},
Signatures: map[string]repodb.ManifestSignatures{
signedManifestDigest.String(): {
sygMeta.SignatureType: []repodb.SignatureInfo{
{
SignatureManifestDigest: sygMeta.SignatureDigest,
LayersInfo: sygMeta.LayersInfo,
},
},
},
},
Statistics: map[string]repodb.DescriptorStatistics{},
Referrers: map[string][]repodb.ReferrerInfo{},
}

repoMetaBlob, err = json.Marshal(repoMeta)
if err != nil {
return err
}

return buck.Put([]byte(repo), repoMetaBlob)
}

var repoMeta repodb.RepoMetadata
Expand All @@ -769,17 +780,10 @@ func (bdw *DBWrapper) AddManifestSignature(repo string, signedManifestDigest god

signatureSlice := manifestSignatures[sygMeta.SignatureType]
if !common.SignatureAlreadyExists(signatureSlice, sygMeta) {
if sygMeta.SignatureType == repodb.NotationType {
signatureSlice = append(signatureSlice, repodb.SignatureInfo{
SignatureManifestDigest: sygMeta.SignatureDigest,
LayersInfo: sygMeta.LayersInfo,
})
} else if sygMeta.SignatureType == repodb.CosignType {
signatureSlice = []repodb.SignatureInfo{{
SignatureManifestDigest: sygMeta.SignatureDigest,
LayersInfo: sygMeta.LayersInfo,
}}
}
signatureSlice = append(signatureSlice, repodb.SignatureInfo{
SignatureManifestDigest: sygMeta.SignatureDigest,
LayersInfo: sygMeta.LayersInfo,
})
}

manifestSignatures[sygMeta.SignatureType] = signatureSlice
Expand Down
Loading

0 comments on commit 45f2c81

Please sign in to comment.