mirror of
https://github.com/goharbor/harbor.git
synced 2024-08-20 11:02:58 +02:00
sync image
This commit is contained in:
parent
c35f6c2b03
commit
4ff5fea5c8
|
@ -1,298 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
|
|
||||||
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 syncimg
|
|
||||||
|
|
||||||
/*
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/docker/distribution"
|
|
||||||
"github.com/docker/distribution/manifest/schema1"
|
|
||||||
"github.com/docker/distribution/manifest/schema2"
|
|
||||||
"github.com/vmware/harbor/job"
|
|
||||||
"github.com/vmware/harbor/models"
|
|
||||||
"github.com/vmware/harbor/utils/registry"
|
|
||||||
"github.com/vmware/harbor/utils/registry/auth"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
//jobType = "sync_img"
|
|
||||||
stateCheck = "check"
|
|
||||||
statePullManifest = "pull_manifest"
|
|
||||||
stateTransferBlob = "transfer_blob"
|
|
||||||
statePushManifest = "push_manifest"
|
|
||||||
)
|
|
||||||
|
|
||||||
func addTransition(sm *job.JobSM) error {
|
|
||||||
base, err := initBaseHandler(sm)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
sm.AddTransition(models.JobRunning, stateCheck, &checker{baseHandler: base})
|
|
||||||
sm.AddTransition(stateCheck, statePullManifest, &manifestPuller{baseHandler: base})
|
|
||||||
sm.AddTransition(statePullManifest, stateTransferBlob, &blobTransfer{baseHandler: base})
|
|
||||||
sm.AddTransition(statePullManifest, models.JobFinished, &job.StatusUpdater{job.DummyHandler{JobID: sm.JobID}, models.JobFinished})
|
|
||||||
sm.AddTransition(stateTransferBlob, statePushManifest, &manifestPusher{baseHandler: base})
|
|
||||||
sm.AddTransition(statePushManifest, statePullManifest, &manifestPuller{baseHandler: base})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type baseHandler struct {
|
|
||||||
project string // project_name
|
|
||||||
repository string // prject_name/repo_name
|
|
||||||
tags []string
|
|
||||||
|
|
||||||
srcURL string // url of source registry
|
|
||||||
srcSecretKey string // secretKey ...
|
|
||||||
|
|
||||||
dstURL string // url of target registry
|
|
||||||
dstUsr string // username ...
|
|
||||||
dstPwd string // password ...
|
|
||||||
|
|
||||||
srcClient *registry.Repository
|
|
||||||
dstClient *registry.Repository
|
|
||||||
|
|
||||||
manifest distribution.Manifest // manifest of tags[0]
|
|
||||||
blobs []string // blobs need to be transferred for tags[0]
|
|
||||||
|
|
||||||
logger *job.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func initBaseHandler(sm *job.JobSM) (*baseHandler, error) {
|
|
||||||
base := &baseHandler{
|
|
||||||
repository: sm.Parms.Repository,
|
|
||||||
srcURL: sm.Parms.LocalRegURL,
|
|
||||||
dstURL: sm.Parms.TargetURL,
|
|
||||||
dstUsr: sm.Parms.TargetUsername,
|
|
||||||
dstPwd: sm.Parms.TargetPassword,
|
|
||||||
logger: &(sm.Logger),
|
|
||||||
}
|
|
||||||
|
|
||||||
base.project = getProjectName(base.repository)
|
|
||||||
|
|
||||||
//TODO using secret key
|
|
||||||
srcCred := auth.NewBasicAuthCredential("admin", "Harbor12345")
|
|
||||||
srcClient, err := registry.NewRepositoryWithCredential(base.repository, base.srcURL, srcCred)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
base.srcClient = srcClient
|
|
||||||
|
|
||||||
dstCred := auth.NewBasicAuthCredential(base.dstUsr, base.dstPwd)
|
|
||||||
dstClient, err := registry.NewRepositoryWithCredential(base.repository, base.dstURL, dstCred)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
base.dstClient = dstClient
|
|
||||||
|
|
||||||
if len(base.tags) == 0 {
|
|
||||||
tags, err := base.srcClient.ListTag()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
base.tags = tags
|
|
||||||
}
|
|
||||||
|
|
||||||
base.logger.Infof("initialization of base handler completed: project: %s, repository: %s, tags: %v, source URL: %s, destination URL: %s, destination user: %s",
|
|
||||||
base.project, base.repository, base.tags, base.srcURL, base.dstURL, base.dstUsr)
|
|
||||||
|
|
||||||
return base, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *baseHandler) Exit() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getProjectName(repository string) string {
|
|
||||||
repository = strings.TrimSpace(repository)
|
|
||||||
repository = strings.TrimRight(repository, "/")
|
|
||||||
return repository[:strings.LastIndex(repository, "/")]
|
|
||||||
}
|
|
||||||
|
|
||||||
type checker struct {
|
|
||||||
*baseHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
// check existence of project, if it does not exist, create it,
|
|
||||||
// if it exists, check whether the user has write privilege to it.
|
|
||||||
func (c *checker) Enter() (string, error) {
|
|
||||||
exist, err := c.projectExist()
|
|
||||||
if err != nil {
|
|
||||||
c.logger.Errorf("an error occurred while checking existence of project %s on %s with user %s : %v", c.project, c.dstURL, c.dstUsr, err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if !exist {
|
|
||||||
if err := c.createProject(); err != nil {
|
|
||||||
c.logger.Errorf("an error occurred while creating project %s on %s with user %s : %v", c.project, c.dstURL, c.dstUsr, err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
c.logger.Infof("project %s is created on %s with user %s", c.project, c.dstURL, c.dstUsr)
|
|
||||||
return statePullManifest, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
c.logger.Infof("project %s already exists on %s", c.project, c.dstURL)
|
|
||||||
canWrite, err := c.canWrite()
|
|
||||||
if err != nil {
|
|
||||||
c.logger.Errorf("an error occurred while checking %s 's privileges to project %s on %s : %v", c.dstUsr, c.project, c.dstURL, err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !canWrite {
|
|
||||||
c.logger.Errorf("the user %s has no write privilege to project %s on %s", c.dstUsr, c.project, c.dstURL)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
c.logger.Infof("the user %s has write privilege to project %s on %s", c.dstUsr, c.project, c.dstURL)
|
|
||||||
|
|
||||||
return statePullManifest, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *checker) projectExist() (bool, error) {
|
|
||||||
exist := true
|
|
||||||
return exist, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *checker) createProject() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *checker) canWrite() (bool, error) {
|
|
||||||
canWrite := true
|
|
||||||
return canWrite, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type manifestPuller struct {
|
|
||||||
*baseHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *manifestPuller) Enter() (string, error) {
|
|
||||||
if len(m.tags) == 0 {
|
|
||||||
m.logger.Infof("no tag needs to be replicated, entering finish state")
|
|
||||||
return models.JobFinished, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
name := m.repository
|
|
||||||
tag := m.tags[0]
|
|
||||||
|
|
||||||
acceptMediaTypes := []string{schema1.MediaTypeManifest, schema2.MediaTypeManifest}
|
|
||||||
digest, mediaType, payload, err := m.srcClient.PullManifest(tag, acceptMediaTypes)
|
|
||||||
if err != nil {
|
|
||||||
m.logger.Errorf("an error occurred while pulling manifest of %s:%s from %s: %v", name, tag, m.srcURL, err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
m.logger.Infof("manifest of %s:%s pulled successfully from %s: %s", name, tag, m.srcURL, digest)
|
|
||||||
|
|
||||||
if strings.Contains(mediaType, "application/json") {
|
|
||||||
mediaType = schema1.MediaTypeManifest
|
|
||||||
}
|
|
||||||
|
|
||||||
manifest, _, err := registry.UnMarshal(mediaType, payload)
|
|
||||||
if err != nil {
|
|
||||||
m.logger.Errorf("an error occurred while parsing manifest of %s:%s from %s: %v", name, tag, m.srcURL, err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
m.manifest = manifest
|
|
||||||
|
|
||||||
// all blobs(layers and config)
|
|
||||||
var blobs []string
|
|
||||||
|
|
||||||
for _, discriptor := range manifest.References() {
|
|
||||||
blobs = append(blobs, discriptor.Digest.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// config is also need to be transferred if the schema of manifest is v2
|
|
||||||
manifest2, ok := manifest.(*schema2.DeserializedManifest)
|
|
||||||
if ok {
|
|
||||||
blobs = append(blobs, manifest2.Target().Digest.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
m.logger.Infof("all blobs of %s:%s from %s: %v", name, tag, m.srcURL, blobs)
|
|
||||||
|
|
||||||
for _, blob := range blobs {
|
|
||||||
exist, err := m.dstClient.BlobExist(blob)
|
|
||||||
if err != nil {
|
|
||||||
m.logger.Errorf("an error occurred while checking existence of blob %s of %s:%s on %s: %v", blob, name, tag, m.dstURL, err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if !exist {
|
|
||||||
m.blobs = append(m.blobs, blob)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m.logger.Infof("blobs of %s:%s need to be transferred to %s: %v", name, tag, m.dstURL, m.blobs)
|
|
||||||
|
|
||||||
m.blobs = blobs
|
|
||||||
|
|
||||||
return stateTransferBlob, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type blobTransfer struct {
|
|
||||||
*baseHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *blobTransfer) Enter() (string, error) {
|
|
||||||
name := b.repository
|
|
||||||
tag := b.tags[0]
|
|
||||||
for _, blob := range b.blobs {
|
|
||||||
size, data, err := b.srcClient.PullBlob(blob)
|
|
||||||
if err != nil {
|
|
||||||
b.logger.Errorf("an error occurred while pulling blob %s of %s:%s from %s: %v", blob, name, tag, b.srcURL, err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if err = b.dstClient.PushBlob(blob, size, data); err != nil {
|
|
||||||
b.logger.Errorf("an error occurred while pushing blob %s of %s:%s to %s : %v", blob, name, tag, b.dstURL, err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
b.logger.Infof("blob %s of %s:%s tranferred to %s completed", blob, name, tag, b.dstURL)
|
|
||||||
}
|
|
||||||
|
|
||||||
return statePushManifest, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type manifestPusher struct {
|
|
||||||
*baseHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *manifestPusher) Enter() (string, error) {
|
|
||||||
name := m.repository
|
|
||||||
tag := m.tags[0]
|
|
||||||
_, exist, err := m.srcClient.ManifestExist(tag)
|
|
||||||
if err != nil {
|
|
||||||
m.logger.Infof("an error occurred while checking the existence of manifest of %s:%s on %s: %v", name, tag, m.srcURL, err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if !exist {
|
|
||||||
m.logger.Infof("manifest of %s:%s does not exist on source registry %s, cancel manifest pushing", name, tag, m.srcURL)
|
|
||||||
} else {
|
|
||||||
m.logger.Infof("manifest of %s:%s exists on source registry %s, continue manifest pushing", name, tag, m.srcURL)
|
|
||||||
mediaType, data, err := m.manifest.Payload()
|
|
||||||
if err != nil {
|
|
||||||
m.logger.Errorf("an error occurred while getting payload of manifest for %s:%s : %v", name, tag, err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if _, err = m.dstClient.PushManifest(tag, mediaType, data); err != nil {
|
|
||||||
m.logger.Errorf("an error occurred while pushing manifest of %s:%s to %s : %v", name, tag, m.dstURL, err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
m.logger.Infof("manifest of %s:%s has been pushed to %s", name, tag, m.dstURL)
|
|
||||||
}
|
|
||||||
|
|
||||||
m.tags = m.tags[1:]
|
|
||||||
|
|
||||||
return statePullManifest, nil
|
|
||||||
}
|
|
||||||
*/
|
|
Loading…
Reference in New Issue
Block a user