mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-27 12:46:03 +01:00
Call Harbor API to delete the images in Harbor adapter
Call Harbor API to delete the images in Harbor adapter to avoid the inconsistent between the different versions of Harbor Signed-off-by: Wenkai Yin <yinw@vmware.com>
This commit is contained in:
parent
68803313d9
commit
5219073c49
@ -37,10 +37,10 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/common/utils/notary"
|
"github.com/goharbor/harbor/src/common/utils/notary"
|
||||||
"github.com/goharbor/harbor/src/common/utils/registry"
|
"github.com/goharbor/harbor/src/common/utils/registry"
|
||||||
"github.com/goharbor/harbor/src/core/config"
|
"github.com/goharbor/harbor/src/core/config"
|
||||||
"github.com/goharbor/harbor/src/core/notifier"
|
|
||||||
coreutils "github.com/goharbor/harbor/src/core/utils"
|
coreutils "github.com/goharbor/harbor/src/core/utils"
|
||||||
"github.com/goharbor/harbor/src/replication/event/notification"
|
"github.com/goharbor/harbor/src/replication/ng"
|
||||||
"github.com/goharbor/harbor/src/replication/event/topic"
|
"github.com/goharbor/harbor/src/replication/ng/event"
|
||||||
|
"github.com/goharbor/harbor/src/replication/ng/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RepositoryAPI handles request to /api/repositories /api/repositories/tags /api/repositories/manifests, the parm has to be put
|
// RepositoryAPI handles request to /api/repositories /api/repositories/tags /api/repositories/manifests, the parm has to be put
|
||||||
@ -328,15 +328,21 @@ func (ra *RepositoryAPI) Delete() {
|
|||||||
log.Infof("delete tag: %s:%s", repoName, t)
|
log.Infof("delete tag: %s:%s", repoName, t)
|
||||||
|
|
||||||
go func(tag string) {
|
go func(tag string) {
|
||||||
image := repoName + ":" + tag
|
e := &event.Event{
|
||||||
err := notifier.Publish(topic.ReplicationEventTopicOnDeletion, notification.OnDeletionNotification{
|
Type: event.EventTypeImagePush,
|
||||||
Image: image,
|
Resource: &model.Resource{
|
||||||
})
|
Type: model.ResourceTypeRepository,
|
||||||
if err != nil {
|
Metadata: &model.ResourceMetadata{
|
||||||
log.Errorf("failed to publish on deletion topic for resource %s: %v", image, err)
|
Name: repoName,
|
||||||
return
|
Namespace: projectName,
|
||||||
|
Vtags: []string{tag},
|
||||||
|
},
|
||||||
|
Deleted: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := ng.EventHandler.Handle(e); err != nil {
|
||||||
|
log.Errorf("failed to handle event: %v", err)
|
||||||
}
|
}
|
||||||
log.Debugf("the on deletion topic for resource %s published", image)
|
|
||||||
}(t)
|
}(t)
|
||||||
|
|
||||||
go func(tag string) {
|
go func(tag string) {
|
||||||
|
@ -66,3 +66,10 @@ func (a *adapter) FetchImages(namespaces []string, filters []*model.Filter) ([]*
|
|||||||
|
|
||||||
return resources, nil
|
return resources, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// override the default implementation from the default image registry
|
||||||
|
// by calling Harbor API directly
|
||||||
|
func (a *adapter) DeleteManifest(repository, reference string) error {
|
||||||
|
url := fmt.Sprintf("%s/api/repositories/%s/tags/%s", a.coreServiceURL, repository, reference)
|
||||||
|
return a.client.Delete(url)
|
||||||
|
}
|
||||||
|
@ -22,15 +22,15 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/replication/ng/util"
|
|
||||||
|
|
||||||
"github.com/docker/distribution"
|
"github.com/docker/distribution"
|
||||||
"github.com/docker/distribution/manifest/schema1"
|
"github.com/docker/distribution/manifest/schema1"
|
||||||
"github.com/goharbor/harbor/src/common/http/modifier"
|
"github.com/goharbor/harbor/src/common/http/modifier"
|
||||||
common_http_auth "github.com/goharbor/harbor/src/common/http/modifier/auth"
|
common_http_auth "github.com/goharbor/harbor/src/common/http/modifier/auth"
|
||||||
|
"github.com/goharbor/harbor/src/common/utils/log"
|
||||||
registry_pkg "github.com/goharbor/harbor/src/common/utils/registry"
|
registry_pkg "github.com/goharbor/harbor/src/common/utils/registry"
|
||||||
"github.com/goharbor/harbor/src/common/utils/registry/auth"
|
"github.com/goharbor/harbor/src/common/utils/registry/auth"
|
||||||
"github.com/goharbor/harbor/src/replication/ng/model"
|
"github.com/goharbor/harbor/src/replication/ng/model"
|
||||||
|
"github.com/goharbor/harbor/src/replication/ng/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// const definition
|
// const definition
|
||||||
@ -45,7 +45,8 @@ type ImageRegistry interface {
|
|||||||
ManifestExist(repository, reference string) (exist bool, digest string, err error)
|
ManifestExist(repository, reference string) (exist bool, digest string, err error)
|
||||||
PullManifest(repository, reference string, accepttedMediaTypes []string) (manifest distribution.Manifest, digest string, err error)
|
PullManifest(repository, reference string, accepttedMediaTypes []string) (manifest distribution.Manifest, digest string, err error)
|
||||||
PushManifest(repository, reference, mediaType string, payload []byte) error
|
PushManifest(repository, reference, mediaType string, payload []byte) error
|
||||||
DeleteManifest(repository, digest string) error
|
// the "reference" can be "tag" or "digest", the function needs to handle both
|
||||||
|
DeleteManifest(repository, reference string) error
|
||||||
BlobExist(repository, digest string) (exist bool, err error)
|
BlobExist(repository, digest string) (exist bool, err error)
|
||||||
PullBlob(repository, digest string) (size int64, blob io.ReadCloser, err error)
|
PullBlob(repository, digest string) (size int64, blob io.ReadCloser, err error)
|
||||||
PushBlob(repository, digest string, size int64, blob io.Reader) error
|
PushBlob(repository, digest string, size int64, blob io.Reader) error
|
||||||
@ -179,15 +180,24 @@ func (d *DefaultImageRegistry) PushManifest(repository, reference, mediaType str
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO monitor the registry API request in core directly rather than using
|
|
||||||
// the web hook
|
|
||||||
|
|
||||||
// DeleteManifest ...
|
// DeleteManifest ...
|
||||||
func (d *DefaultImageRegistry) DeleteManifest(repository, digest string) error {
|
func (d *DefaultImageRegistry) DeleteManifest(repository, reference string) error {
|
||||||
client, err := d.getClient(repository)
|
client, err := d.getClient(repository)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
digest := reference
|
||||||
|
if !isDigest(digest) {
|
||||||
|
dgt, exist, err := client.ManifestExist(reference)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !exist {
|
||||||
|
log.Debugf("the manifest of %s:%s doesn't exist", repository, reference)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
digest = dgt
|
||||||
|
}
|
||||||
return client.DeleteManifest(digest)
|
return client.DeleteManifest(digest)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,3 +227,7 @@ func (d *DefaultImageRegistry) PushBlob(repository, digest string, size int64, b
|
|||||||
}
|
}
|
||||||
return client.PushBlob(digest, size, blob)
|
return client.PushBlob(digest, size, blob)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isDigest(str string) bool {
|
||||||
|
return strings.Contains(str, ":")
|
||||||
|
}
|
||||||
|
46
src/replication/ng/adapter/image_registry_test.go
Normal file
46
src/replication/ng/adapter/image_registry_test.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// 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 adapter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO add UT
|
||||||
|
|
||||||
|
func TestIsDigest(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
str string
|
||||||
|
isDigest bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
str: "",
|
||||||
|
isDigest: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
str: "latest",
|
||||||
|
isDigest: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
str: "sha256:fea8895f450959fa676bcc1df0611ea93823a735a01205fd8622846041d0c7cf",
|
||||||
|
isDigest: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
assert.Equal(t, c.isDigest, isDigest(c.str))
|
||||||
|
}
|
||||||
|
}
|
@ -278,10 +278,9 @@ func (t *transfer) delete(repo *repository) error {
|
|||||||
return jobStoppedErr
|
return jobStoppedErr
|
||||||
}
|
}
|
||||||
|
|
||||||
digests := map[string]struct{}{}
|
|
||||||
repository := repo.repository
|
repository := repo.repository
|
||||||
for _, tag := range repo.tags {
|
for _, tag := range repo.tags {
|
||||||
exist, digest, err := t.dst.ManifestExist(repository, tag)
|
exist, _, err := t.dst.ManifestExist(repository, tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.logger.Errorf("failed to check the existence of the manifest of image %s:%s on the destination registry: %v",
|
t.logger.Errorf("failed to check the existence of the manifest of image %s:%s on the destination registry: %v",
|
||||||
repository, tag, err)
|
repository, tag, err)
|
||||||
@ -292,18 +291,12 @@ func (t *transfer) delete(repo *repository) error {
|
|||||||
repository, tag)
|
repository, tag)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
t.logger.Infof("the digest of image %s:%s is %s", repository, tag, digest)
|
if err := t.dst.DeleteManifest(repository, tag); err != nil {
|
||||||
if _, exist := digests[digest]; !exist {
|
|
||||||
digests[digest] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for digest := range digests {
|
|
||||||
if err := t.dst.DeleteManifest(repository, digest); err != nil {
|
|
||||||
t.logger.Errorf("failed to delete the manifest of image %s:%s on the destination registry: %v",
|
t.logger.Errorf("failed to delete the manifest of image %s:%s on the destination registry: %v",
|
||||||
repository, digest, err)
|
repository, tag, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t.logger.Infof("the manifest of image %s:%s is deleted", repository, digest)
|
t.logger.Infof("the manifest of image %s:%s is deleted", repository, tag)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user