Implement the index/manifest list image resolver

The index/manifest list image resolver populates the references of the artifact

Signed-off-by: Wenkai Yin <yinw@vmware.com>
This commit is contained in:
Wenkai Yin 2020-01-11 16:02:16 +08:00
parent df551e1310
commit 40d5043d5c
2 changed files with 171 additions and 4 deletions

View File

@ -12,15 +12,20 @@ package image
import ( import (
"context" "context"
"encoding/json"
"fmt"
"github.com/docker/distribution/manifest/manifestlist" "github.com/docker/distribution/manifest/manifestlist"
"github.com/goharbor/harbor/src/api/artifact/abstractor/resolver" "github.com/goharbor/harbor/src/api/artifact/abstractor/resolver"
"github.com/goharbor/harbor/src/common/utils/log" "github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/pkg/artifact" "github.com/goharbor/harbor/src/pkg/artifact"
"github.com/goharbor/harbor/src/pkg/q"
v1 "github.com/opencontainers/image-spec/specs-go/v1" v1 "github.com/opencontainers/image-spec/specs-go/v1"
) )
func init() { func init() {
rslver := &indexResolver{} rslver := &indexResolver{
artMgr: artifact.Mgr,
}
if err := resolver.Register(rslver, v1.MediaTypeImageIndex, manifestlist.MediaTypeManifestList); err != nil { if err := resolver.Register(rslver, v1.MediaTypeImageIndex, manifestlist.MediaTypeManifestList); err != nil {
log.Errorf("failed to register resolver for artifact %s: %v", rslver.ArtifactType(), err) log.Errorf("failed to register resolver for artifact %s: %v", rslver.ArtifactType(), err)
return return
@ -29,14 +34,39 @@ func init() {
// indexResolver resolves artifact with OCI index and docker manifest list // indexResolver resolves artifact with OCI index and docker manifest list
type indexResolver struct { type indexResolver struct {
artMgr artifact.Manager
} }
func (i *indexResolver) ArtifactType() string { func (i *indexResolver) ArtifactType() string {
return ArtifactTypeImage return ArtifactTypeImage
} }
func (i *indexResolver) Resolve(ctx context.Context, manifest []byte, artifact *artifact.Artifact) error { func (i *indexResolver) Resolve(ctx context.Context, manifest []byte, art *artifact.Artifact) error {
// TODO implement index := &v1.Index{}
// how to make sure the artifact referenced by the index has already been saved in database if err := json.Unmarshal(manifest, index); err != nil {
return err
}
// populate the referenced artifacts
for _, mani := range index.Manifests {
digest := mani.Digest.String()
_, arts, err := i.artMgr.List(ctx, &q.Query{
Keywords: map[string]interface{}{
"RepositoryID": art.RepositoryID,
"Digest": digest,
},
})
if err != nil {
return err
}
// make sure the child artifact exist
if len(arts) == 0 {
return fmt.Errorf("the referenced artifact with digest %s not found under repository %d",
digest, art.RepositoryID)
}
art.References = append(art.References, &artifact.Reference{
ChildID: arts[0].ID,
Platform: mani.Platform,
})
}
return nil return nil
} }

View File

@ -0,0 +1,137 @@
// 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 image
import (
"github.com/goharbor/harbor/src/pkg/artifact"
htesting "github.com/goharbor/harbor/src/testing"
"github.com/stretchr/testify/suite"
"testing"
)
type indexResolverTestSuite struct {
suite.Suite
resolver *indexResolver
artMgr *htesting.FakeArtifactManager
}
func (i *indexResolverTestSuite) SetupTest() {
i.artMgr = &htesting.FakeArtifactManager{}
i.resolver = &indexResolver{
artMgr: i.artMgr,
}
}
func (i *indexResolverTestSuite) TestArtifactType() {
i.Assert().Equal(ArtifactTypeImage, i.resolver.ArtifactType())
}
func (i *indexResolverTestSuite) TestResolve() {
manifest := `{
"manifests": [
{
"digest": "sha256:92c7f9c92844bbbb5d0a101b22f7c2a7949e40f8ea90c8b3bc396879d95e899a",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "amd64",
"os": "linux"
},
"size": 524
},
{
"digest": "sha256:e5785cb0c62cebbed4965129bae371f0589cadd6d84798fb58c2c5f9e237efd9",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v5"
},
"size": 525
},
{
"digest": "sha256:50b8560ad574c779908da71f7ce370c0a2471c098d44d1c8f6b513c5a55eeeb1",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v7"
},
"size": 525
},
{
"digest": "sha256:963612c5503f3f1674f315c67089dee577d8cc6afc18565e0b4183ae355fb343",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm64",
"os": "linux",
"variant": "v8"
},
"size": 525
},
{
"digest": "sha256:85dc5fbe16214366748ebe9d7cc73bc42d61d19d61fe05f01e317d278c2287ed",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "386",
"os": "linux"
},
"size": 527
},
{
"digest": "sha256:8aaea2a718a29334caeaf225716284ce29dc17418edba98dbe6dafea5afcda16",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "ppc64le",
"os": "linux"
},
"size": 525
},
{
"digest": "sha256:577ad4331d4fac91807308da99ecc107dcc6b2254bc4c7166325fd01113bea2a",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "s390x",
"os": "linux"
},
"size": 525
},
{
"digest": "sha256:351e40a9ab7ca6818dfbf9c967d1dd15599438edc41189e3d4d87eeffba5b8bf",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "amd64",
"os": "windows",
"os.version": "10.0.17763.914"
},
"size": 1124
}
],
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
"schemaVersion": 2
}`
art := &artifact.Artifact{}
i.artMgr.On("List").Return(1, []*artifact.Artifact{
{
ID: 1,
},
}, nil)
err := i.resolver.Resolve(nil, []byte(manifest), art)
i.Require().Nil(err)
i.artMgr.AssertExpectations(i.T())
i.Assert().Len(art.References, 8)
i.Assert().Equal(int64(1), art.References[0].ChildID)
i.Assert().Equal("amd64", art.References[0].Platform.Architecture)
}
func TestIndexResolverTestSuite(t *testing.T) {
suite.Run(t, &indexResolverTestSuite{})
}