mirror of https://github.com/goharbor/harbor.git
163 lines
5.7 KiB
Go
163 lines
5.7 KiB
Go
// 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 proxy
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
|
|
"github.com/docker/distribution"
|
|
|
|
"github.com/goharbor/harbor/src/controller/artifact"
|
|
"github.com/goharbor/harbor/src/controller/event/metadata"
|
|
"github.com/goharbor/harbor/src/lib"
|
|
"github.com/goharbor/harbor/src/lib/cache"
|
|
"github.com/goharbor/harbor/src/lib/config"
|
|
"github.com/goharbor/harbor/src/lib/errors"
|
|
"github.com/goharbor/harbor/src/lib/log"
|
|
"github.com/goharbor/harbor/src/pkg/notifier/event"
|
|
"github.com/goharbor/harbor/src/pkg/proxy/secret"
|
|
"github.com/goharbor/harbor/src/pkg/registry"
|
|
)
|
|
|
|
// TrimmedManifestlist - key prefix for trimmed manifest
|
|
const TrimmedManifestlist = "trimmedmanifestlist:"
|
|
|
|
// localInterface defines operations related to local repo under proxy mode
|
|
type localInterface interface {
|
|
// BlobExist check if the blob exist in local repo
|
|
BlobExist(ctx context.Context, art lib.ArtifactInfo) (bool, error)
|
|
// GetManifest get the manifest info
|
|
GetManifest(ctx context.Context, art lib.ArtifactInfo) (*artifact.Artifact, error)
|
|
// PushBlob push blob to local repo
|
|
PushBlob(localRepo string, desc distribution.Descriptor, bReader io.ReadCloser) error
|
|
// PushManifest push manifest to local repo, ref can be digest or tag
|
|
PushManifest(repo string, ref string, manifest distribution.Manifest) error
|
|
// CheckDependencies check if the manifest's dependency is ready
|
|
CheckDependencies(ctx context.Context, repo string, man distribution.Manifest) []distribution.Descriptor
|
|
// DeleteManifest cleanup delete tag from local cache
|
|
DeleteManifest(repo, ref string)
|
|
}
|
|
|
|
func (l *localHelper) GetManifest(ctx context.Context, art lib.ArtifactInfo) (*artifact.Artifact, error) {
|
|
ref := getReference(art)
|
|
a, err := l.artifactCtl.GetByReference(ctx, art.Repository, ref, nil)
|
|
if err != nil {
|
|
if errors.IsNotFoundErr(err) {
|
|
return nil, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
// localHelper defines operations related to local repo under proxy mode
|
|
type localHelper struct {
|
|
registry registry.Client
|
|
artifactCtl artifactController
|
|
cache cache.Cache
|
|
}
|
|
|
|
type artifactController interface {
|
|
GetByReference(ctx context.Context, repository, reference string, option *artifact.Option) (artifact *artifact.Artifact, err error)
|
|
}
|
|
|
|
// newLocalHelper create the localInterface
|
|
func newLocalHelper() localInterface {
|
|
l := &localHelper{artifactCtl: artifact.Ctl}
|
|
l.init()
|
|
return l
|
|
}
|
|
|
|
func (l *localHelper) BlobExist(_ context.Context, art lib.ArtifactInfo) (bool, error) {
|
|
return l.registry.BlobExist(art.Repository, art.Digest)
|
|
}
|
|
|
|
func (l *localHelper) init() {
|
|
if l.registry != nil {
|
|
return
|
|
}
|
|
log.Debugf("core url:%s, local core url: %v", config.GetCoreURL(), config.LocalCoreURL())
|
|
// the traffic is internal only
|
|
registryURL := config.LocalCoreURL()
|
|
l.registry = registry.NewClientWithAuthorizer(registryURL, secret.NewAuthorizer(), true)
|
|
l.cache = cache.Default()
|
|
}
|
|
|
|
func (l *localHelper) PushBlob(localRepo string, desc distribution.Descriptor, bReader io.ReadCloser) error {
|
|
log.Debugf("Put blob to local registry, localRepo:%v, digest: %v", localRepo, desc.Digest)
|
|
ref := string(desc.Digest)
|
|
artName := localRepo + ":" + ref
|
|
// use inflight checker to avoid multiple requests to push blob to local in same time
|
|
if !inflightChecker.addRequest(artName) {
|
|
return nil
|
|
}
|
|
defer inflightChecker.removeRequest(artName)
|
|
err := l.registry.PushBlob(localRepo, ref, desc.Size, bReader)
|
|
return err
|
|
}
|
|
|
|
func (l *localHelper) PushManifest(repo string, ref string, manifest distribution.Manifest) error {
|
|
// Make sure there is only one go routing to push current artName to local repo
|
|
artName := repo + ":" + ref
|
|
// use inflight checker to avoid multiple requests to push manifest to local in same time
|
|
if !inflightChecker.addRequest(artName) {
|
|
return nil
|
|
}
|
|
defer inflightChecker.removeRequest(artName)
|
|
|
|
mediaType, payload, err := manifest.Payload()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = l.registry.PushManifest(repo, ref, mediaType, payload)
|
|
return err
|
|
}
|
|
|
|
// DeleteManifest cleanup delete tag from local repo
|
|
func (l *localHelper) DeleteManifest(repo, ref string) {
|
|
log.Debugf("Remove tag from repo if it is exist, repo: %v ref: %v", repo, ref)
|
|
if err := l.registry.DeleteManifest(repo, ref); err != nil {
|
|
// sometimes user pull a non-exist image
|
|
log.Warningf("failed to remove artifact, error %v", err)
|
|
}
|
|
}
|
|
|
|
func (l *localHelper) CheckDependencies(ctx context.Context, repo string, man distribution.Manifest) []distribution.Descriptor {
|
|
descriptors := man.References()
|
|
waitDesc := make([]distribution.Descriptor, 0)
|
|
for _, desc := range descriptors {
|
|
log.Debugf("checking the blob dependency: %v", desc.Digest)
|
|
art := lib.ArtifactInfo{Repository: repo, Digest: string(desc.Digest)}
|
|
exist, err := l.BlobExist(ctx, art)
|
|
if err != nil || !exist {
|
|
log.Debugf("Check dependency failed!")
|
|
waitDesc = append(waitDesc, desc)
|
|
}
|
|
}
|
|
log.Debugf("Check dependency result %v", waitDesc)
|
|
return waitDesc
|
|
}
|
|
|
|
// SendPullEvent send a pull image event
|
|
func SendPullEvent(ctx context.Context, a *artifact.Artifact, tag, operator string) {
|
|
e := &metadata.PullArtifactEventMetadata{
|
|
Artifact: &a.Artifact,
|
|
Tag: tag,
|
|
Operator: operator,
|
|
}
|
|
event.BuildAndPublish(ctx, e)
|
|
}
|