feat: add the cnai model processor for artifact

Signed-off-by: chlins <chlins.zhang@gmail.com>
This commit is contained in:
chlins 2024-10-30 16:56:40 +08:00
parent 3dbfd4229b
commit cadd66936b
6 changed files with 127 additions and 6 deletions

BIN
icons/model.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

View File

@ -83,7 +83,7 @@ func (a *abstractor) AbstractMetadata(ctx context.Context, artifact *artifact.Ar
default:
return fmt.Errorf("unsupported manifest media type: %s", artifact.ManifestMediaType)
}
return processor.Get(artifact.MediaType).AbstractMetadata(ctx, artifact, content)
return processor.Get(artifact.ArtifactType).AbstractMetadata(ctx, artifact, content)
}
// the artifact is enveloped by docker manifest v1

View File

@ -29,6 +29,7 @@ import (
"github.com/goharbor/harbor/src/controller/artifact/processor/chart"
"github.com/goharbor/harbor/src/controller/artifact/processor/cnab"
"github.com/goharbor/harbor/src/controller/artifact/processor/image"
"github.com/goharbor/harbor/src/controller/artifact/processor/model"
"github.com/goharbor/harbor/src/controller/artifact/processor/sbom"
"github.com/goharbor/harbor/src/controller/artifact/processor/wasm"
"github.com/goharbor/harbor/src/controller/event/metadata"
@ -44,7 +45,7 @@ import (
accessorymodel "github.com/goharbor/harbor/src/pkg/accessory/model"
"github.com/goharbor/harbor/src/pkg/artifact"
"github.com/goharbor/harbor/src/pkg/artifactrash"
"github.com/goharbor/harbor/src/pkg/artifactrash/model"
trashmodel "github.com/goharbor/harbor/src/pkg/artifactrash/model"
"github.com/goharbor/harbor/src/pkg/blob"
"github.com/goharbor/harbor/src/pkg/immutable/match"
"github.com/goharbor/harbor/src/pkg/immutable/match/rule"
@ -78,6 +79,7 @@ var (
cnab.ArtifactTypeCNAB: icon.DigestOfIconCNAB,
wasm.ArtifactTypeWASM: icon.DigestOfIconWASM,
sbom.ArtifactTypeSBOM: icon.DigestOfIconAccSBOM,
model.ArtifactTypeModel: icon.DigestOfIconModel,
}
)
@ -219,7 +221,7 @@ func (c *controller) ensureArtifact(ctx context.Context, repository, digest stri
}
// populate the artifact type
artifact.Type = processor.Get(artifact.MediaType).GetArtifactType(ctx, artifact)
artifact.Type = processor.Get(artifact.ArtifactType).GetArtifactType(ctx, artifact)
// create it
// use orm.WithTransaction here to avoid the issue:
@ -437,7 +439,7 @@ func (c *controller) deleteDeeply(ctx context.Context, id int64, isRoot, isAcces
// use orm.WithTransaction here to avoid the issue:
// https://www.postgresql.org/message-id/002e01c04da9%24a8f95c20%2425efe6c1%40lasting.ro
if err = orm.WithTransaction(func(ctx context.Context) error {
_, err = c.artrashMgr.Create(ctx, &model.ArtifactTrash{
_, err = c.artrashMgr.Create(ctx, &trashmodel.ArtifactTrash{
MediaType: art.MediaType,
ManifestMediaType: art.ManifestMediaType,
RepositoryName: art.RepositoryName,
@ -593,7 +595,7 @@ func (c *controller) GetAddition(ctx context.Context, artifactID int64, addition
if err != nil {
return nil, err
}
return processor.Get(artifact.MediaType).AbstractAddition(ctx, artifact, addition)
return processor.Get(artifact.ArtifactType).AbstractAddition(ctx, artifact, addition)
}
func (c *controller) AddLabel(ctx context.Context, artifactID int64, labelID int64) (err error) {
@ -751,7 +753,7 @@ func (c *controller) populateLabels(ctx context.Context, art *Artifact) {
}
func (c *controller) populateAdditionLinks(ctx context.Context, artifact *Artifact) {
types := processor.Get(artifact.MediaType).ListAdditionTypes(ctx, &artifact.Artifact)
types := processor.Get(artifact.ArtifactType).ListAdditionTypes(ctx, &artifact.Artifact)
if len(types) > 0 {
version := lib.GetAPIVersion(ctx)
for _, t := range types {

View File

@ -0,0 +1,114 @@
// Copyright Project Harbor Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package model
import (
"context"
"encoding/json"
"io"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
ps "github.com/goharbor/harbor/src/controller/artifact/processor"
"github.com/goharbor/harbor/src/controller/artifact/processor/base"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/pkg/artifact"
)
// const definitions
const (
// ArtifactTypeModel defines the artifact type for AI models
ArtifactTypeModel = "MODEL"
AdditionTypeReadme = "README.MD"
// TODO: import from cnai model spec
artifactType = "application/vnd.cnai.model.manifest.v1+json"
)
func init() {
pc := &processor{
ManifestProcessor: base.NewManifestProcessor(),
}
if err := ps.Register(pc, artifactType); err != nil {
log.Errorf("failed to register processor for artifact type %s: %v", artifactType, err)
return
}
}
type processor struct {
*base.ManifestProcessor
}
func (p *processor) AbstractAddition(_ context.Context, artifact *artifact.Artifact, addition string) (*ps.Addition, error) {
if addition != AdditionTypeReadme {
return nil, errors.New(nil).WithCode(errors.BadRequestCode).
WithMessagef("addition %s isn't supported for %s", addition, ArtifactTypeModel)
}
m, _, err := p.RegCli.PullManifest(artifact.RepositoryName, artifact.Digest)
if err != nil {
return nil, err
}
_, payload, err := m.Payload()
if err != nil {
return nil, err
}
manifest := &v1.Manifest{}
if err := json.Unmarshal(payload, manifest); err != nil {
return nil, err
}
for _, layer := range manifest.Layers {
layerDgst := layer.Digest.String()
// currently, we only handle readme addition for model artifact.
if layerDgst != manifest.Config.Digest.String() &&
(layer.Annotations != nil && layer.Annotations["org.cnai.model.readme"] == "true") {
_, blob, err := p.RegCli.PullBlob(artifact.RepositoryName, layerDgst)
if err != nil {
return nil, err
}
defer blob.Close()
content, err := io.ReadAll(blob)
if err != nil {
return nil, err
}
var additionContent []byte
var additionContentType string
switch addition {
case AdditionTypeReadme:
additionContent = []byte(content)
additionContentType = "text/markdown; charset=utf-8"
}
return &ps.Addition{
Content: additionContent,
ContentType: additionContentType,
}, nil
}
}
return nil, nil
}
func (p *processor) GetArtifactType(_ context.Context, _ *artifact.Artifact) string {
return ArtifactTypeModel
}
func (p *processor) ListAdditionTypes(_ context.Context, _ *artifact.Artifact) []string {
return []string{AdditionTypeReadme}
}

View File

@ -77,6 +77,10 @@ var (
path: "./icons/default.png",
resize: true,
},
icon.DigestOfIconModel: {
path: "./icons/model.png",
resize: true,
},
}
// Ctl is a global icon controller instance
Ctl = NewController()

View File

@ -28,4 +28,5 @@ const (
DigestOfIconAccNotation = "sha256:3ac706e102bbe9362b400aa162df58135d35e66b9c3bee2165de92022d25fe34"
DigestOfIconAccNydus = "sha256:dfcb6617cd9c144358dc1b305b87bbe34f0b619f1e329116e6aee2e41f2e34cf"
DigestOfIconAccSBOM = "sha256:c19f80c357cd7e90d2a01b9ae3e2eb62ce447a2662bb590a19177d72d550bdae"
DigestOfIconModel = "sha256:9f51c0a8800e98c84952264755c956dba448f2d62112eff5c5a4ef88cd460d1e"
)