mirror of
https://github.com/project-zot/zot.git
synced 2026-06-17 12:58:02 +08:00
feat(artifact): add OCI references support (#936)
Thanks @jdolitsky et al for kicking off these changes at: https://github.com/oci-playground/zot/commits/main Thanks @sudo-bmitch for reviewing the patch Signed-off-by: Ramkumar Chinchani <rchincha@cisco.com>
This commit is contained in:
committed by
GitHub
parent
eb722905cb
commit
c0f93caacb
+130
-11
@@ -20,10 +20,11 @@ import (
|
||||
guuid "github.com/gofrs/uuid"
|
||||
"github.com/minio/sha256-simd"
|
||||
godigest "github.com/opencontainers/go-digest"
|
||||
imeta "github.com/opencontainers/image-spec/specs-go"
|
||||
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/opencontainers/umoci"
|
||||
"github.com/opencontainers/umoci/oci/casext"
|
||||
artifactspec "github.com/oras-project/artifacts-spec/specs-go/v1"
|
||||
oras "github.com/oras-project/artifacts-spec/specs-go/v1"
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
zerr "zotregistry.io/zot/errors"
|
||||
@@ -37,8 +38,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultFilePerms = 0o600
|
||||
DefaultDirPerms = 0o700
|
||||
DefaultFilePerms = 0o600
|
||||
DefaultDirPerms = 0o700
|
||||
defaultSchemaVersion = 2
|
||||
)
|
||||
|
||||
// ImageStoreLocal provides the image storage operations.
|
||||
@@ -186,8 +188,7 @@ func (is *ImageStoreLocal) initRepo(name string) error {
|
||||
// "index.json" file - create if it doesn't exist
|
||||
indexPath := path.Join(repoDir, "index.json")
|
||||
if _, err := os.Stat(indexPath); err != nil {
|
||||
index := ispec.Index{}
|
||||
index.SchemaVersion = 2
|
||||
index := ispec.Index{Versioned: imeta.Versioned{SchemaVersion: defaultSchemaVersion}}
|
||||
|
||||
buf, err := json.Marshal(index)
|
||||
if err != nil {
|
||||
@@ -1339,7 +1340,120 @@ func (is *ImageStoreLocal) DeleteBlob(repo string, digest godigest.Digest) error
|
||||
}
|
||||
|
||||
func (is *ImageStoreLocal) GetReferrers(repo string, gdigest godigest.Digest, artifactType string,
|
||||
) ([]artifactspec.Descriptor, error) {
|
||||
) (ispec.Index, error) {
|
||||
var lockLatency time.Time
|
||||
|
||||
nilIndex := ispec.Index{}
|
||||
|
||||
if err := gdigest.Validate(); err != nil {
|
||||
return nilIndex, err
|
||||
}
|
||||
|
||||
dir := path.Join(is.rootDir, repo)
|
||||
if !is.DirExists(dir) {
|
||||
return nilIndex, zerr.ErrRepoNotFound
|
||||
}
|
||||
|
||||
index, err := storage.GetIndex(is, repo, is.log)
|
||||
if err != nil {
|
||||
return nilIndex, err
|
||||
}
|
||||
|
||||
is.RLock(&lockLatency)
|
||||
defer is.RUnlock(&lockLatency)
|
||||
|
||||
found := false
|
||||
|
||||
result := []ispec.Descriptor{}
|
||||
|
||||
for _, manifest := range index.Manifests {
|
||||
if manifest.Digest == gdigest {
|
||||
continue
|
||||
}
|
||||
|
||||
p := path.Join(dir, "blobs", manifest.Digest.Algorithm().String(), manifest.Digest.Encoded())
|
||||
|
||||
buf, err := os.ReadFile(p)
|
||||
if err != nil {
|
||||
is.log.Error().Err(err).Str("blob", p).Msg("failed to read manifest")
|
||||
|
||||
if os.IsNotExist(err) {
|
||||
return nilIndex, zerr.ErrManifestNotFound
|
||||
}
|
||||
|
||||
return nilIndex, err
|
||||
}
|
||||
|
||||
if manifest.MediaType == ispec.MediaTypeImageManifest {
|
||||
var mfst ispec.Manifest
|
||||
if err := json.Unmarshal(buf, &mfst); err != nil {
|
||||
return nilIndex, err
|
||||
}
|
||||
|
||||
if mfst.Subject == nil || mfst.Subject.Digest != gdigest {
|
||||
continue
|
||||
}
|
||||
|
||||
// filter by artifact type
|
||||
if artifactType != "" && mfst.Config.MediaType != artifactType {
|
||||
continue
|
||||
}
|
||||
|
||||
result = append(result, ispec.Descriptor{
|
||||
MediaType: manifest.MediaType,
|
||||
ArtifactType: mfst.Config.MediaType,
|
||||
Size: manifest.Size,
|
||||
Digest: manifest.Digest,
|
||||
Annotations: mfst.Annotations,
|
||||
})
|
||||
} else if manifest.MediaType == ispec.MediaTypeArtifactManifest {
|
||||
var art ispec.Artifact
|
||||
if err := json.Unmarshal(buf, &art); err != nil {
|
||||
return nilIndex, err
|
||||
}
|
||||
|
||||
if art.Subject == nil || art.Subject.Digest != gdigest {
|
||||
continue
|
||||
}
|
||||
|
||||
// filter by artifact type
|
||||
if artifactType != "" && art.ArtifactType != artifactType {
|
||||
continue
|
||||
}
|
||||
|
||||
result = append(result, ispec.Descriptor{
|
||||
MediaType: manifest.MediaType,
|
||||
ArtifactType: art.ArtifactType,
|
||||
Size: manifest.Size,
|
||||
Digest: manifest.Digest,
|
||||
Annotations: art.Annotations,
|
||||
})
|
||||
}
|
||||
|
||||
found = true
|
||||
}
|
||||
|
||||
if !found {
|
||||
return nilIndex, zerr.ErrManifestNotFound
|
||||
}
|
||||
|
||||
index = ispec.Index{
|
||||
Versioned: imeta.Versioned{SchemaVersion: defaultSchemaVersion},
|
||||
MediaType: ispec.MediaTypeImageIndex,
|
||||
Manifests: result,
|
||||
Annotations: map[string]string{},
|
||||
}
|
||||
|
||||
// response was filtered by artifactType
|
||||
if artifactType != "" {
|
||||
index.Annotations[storageConstants.ReferrerFilterAnnotation] = artifactType
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
|
||||
func (is *ImageStoreLocal) GetOrasReferrers(repo string, gdigest godigest.Digest, artifactType string,
|
||||
) ([]oras.Descriptor, error) {
|
||||
var lockLatency time.Time
|
||||
|
||||
if err := gdigest.Validate(); err != nil {
|
||||
@@ -1361,10 +1475,10 @@ func (is *ImageStoreLocal) GetReferrers(repo string, gdigest godigest.Digest, ar
|
||||
|
||||
found := false
|
||||
|
||||
result := []artifactspec.Descriptor{}
|
||||
result := []oras.Descriptor{}
|
||||
|
||||
for _, manifest := range index.Manifests {
|
||||
if manifest.MediaType != artifactspec.MediaTypeArtifactManifest {
|
||||
if manifest.MediaType != oras.MediaTypeArtifactManifest {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -1381,18 +1495,23 @@ func (is *ImageStoreLocal) GetReferrers(repo string, gdigest godigest.Digest, ar
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var artManifest artifactspec.Manifest
|
||||
var artManifest oras.Manifest
|
||||
if err := json.Unmarshal(buf, &artManifest); err != nil {
|
||||
is.log.Error().Err(err).Str("dir", dir).Msg("invalid JSON")
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if artifactType != artManifest.ArtifactType || gdigest != artManifest.Subject.Digest {
|
||||
if artManifest.Subject.Digest != gdigest {
|
||||
continue
|
||||
}
|
||||
|
||||
result = append(result, artifactspec.Descriptor{
|
||||
// filter by artifact type
|
||||
if artifactType != "" && artManifest.ArtifactType != artifactType {
|
||||
continue
|
||||
}
|
||||
|
||||
result = append(result, oras.Descriptor{
|
||||
MediaType: manifest.MediaType,
|
||||
ArtifactType: artManifest.ArtifactType,
|
||||
Digest: manifest.Digest,
|
||||
|
||||
@@ -147,14 +147,14 @@ func TestStorageFSAPIs(t *testing.T) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// invalid GetReferrers
|
||||
_, err = imgStore.GetReferrers("invalid", "invalid", "invalid")
|
||||
// invalid GetOrasReferrers
|
||||
_, err = imgStore.GetOrasReferrers("invalid", "invalid", "invalid")
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
_, err = imgStore.GetReferrers(repoName, "invalid", "invalid")
|
||||
_, err = imgStore.GetOrasReferrers(repoName, "invalid", "invalid")
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
_, err = imgStore.GetReferrers(repoName, digest, "invalid")
|
||||
_, err = imgStore.GetOrasReferrers(repoName, digest, "invalid")
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
// invalid DeleteImageManifest
|
||||
@@ -175,7 +175,7 @@ func TestStorageFSAPIs(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetReferrers(t *testing.T) {
|
||||
func TestGetOrasReferrers(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
log := log.Logger{Logger: zerolog.New(os.Stdout)}
|
||||
@@ -218,7 +218,7 @@ func TestGetReferrers(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
descriptors, err := imgStore.GetReferrers("zot-test", digest, "signature-example")
|
||||
descriptors, err := imgStore.GetOrasReferrers("zot-test", digest, "signature-example")
|
||||
So(err, ShouldBeNil)
|
||||
So(descriptors, ShouldNotBeEmpty)
|
||||
So(descriptors[0].ArtifactType, ShouldEqual, "signature-example")
|
||||
@@ -982,7 +982,7 @@ func FuzzGetBlobContent(f *testing.F) {
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzGetReferrers(f *testing.F) {
|
||||
func FuzzGetOrasReferrers(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data string) {
|
||||
log := &log.Logger{Logger: zerolog.New(os.Stdout)}
|
||||
metrics := monitoring.NewMetricsServer(false, *log)
|
||||
@@ -1033,7 +1033,7 @@ func FuzzGetReferrers(f *testing.F) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
_, err = imgStore.GetReferrers("zot-test", digest, data)
|
||||
_, err = imgStore.GetOrasReferrers("zot-test", digest, data)
|
||||
if err != nil {
|
||||
if errors.Is(err, zerr.ErrManifestNotFound) || isKnownErr(err) {
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user