From 2c39cda4c13efc2bccdd2d2c4a1dc1a38d332fd0 Mon Sep 17 00:00:00 2001 From: Maksym Trofimenko Date: Sun, 22 Sep 2024 18:56:04 +0100 Subject: [PATCH 1/9] initial commit adds adapter --- .../job/impl/replication/replication.go | 2 + src/pkg/reg/adapter/list-export/adapter.go | 183 ++++++++++++++++++ src/pkg/reg/manager.go | 2 + src/pkg/reg/model/registry.go | 5 +- 4 files changed, 190 insertions(+), 2 deletions(-) create mode 100644 src/pkg/reg/adapter/list-export/adapter.go diff --git a/src/jobservice/job/impl/replication/replication.go b/src/jobservice/job/impl/replication/replication.go index 453fa5e032b..058d97fb832 100644 --- a/src/jobservice/job/impl/replication/replication.go +++ b/src/jobservice/job/impl/replication/replication.go @@ -53,6 +53,8 @@ import ( _ "github.com/goharbor/harbor/src/pkg/reg/adapter/tencentcr" // register the VolcEngine CR Registry adapter _ "github.com/goharbor/harbor/src/pkg/reg/adapter/volcenginecr" + // artifact list exporter + _ "github.com/goharbor/harbor/src/pkg/reg/adapter/list-export" "github.com/goharbor/harbor/src/pkg/reg/model" ) diff --git a/src/pkg/reg/adapter/list-export/adapter.go b/src/pkg/reg/adapter/list-export/adapter.go new file mode 100644 index 00000000000..0d03ca619bb --- /dev/null +++ b/src/pkg/reg/adapter/list-export/adapter.go @@ -0,0 +1,183 @@ +package list_export + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/docker/distribution" + "github.com/goharbor/harbor/src/lib/errors" + adp "github.com/goharbor/harbor/src/pkg/reg/adapter" + regadapter "github.com/goharbor/harbor/src/pkg/reg/adapter" + "github.com/goharbor/harbor/src/pkg/reg/model" + "io" + "net/http" +) + +var ( + _ adp.Adapter = (*adapter)(nil) + _ adp.ArtifactRegistry = (*adapter)(nil) +) +var ErrNotImplemented = errors.New("not implemented") + +type Result struct { + Registry string `json:"registry"` + Artifacts []Artifact `json:"artifacts"` +} + +type Artifact struct { + Repository string `json:"repository"` + Tags []string `json:"tag"` + Type string `json:"type"` + Digest string `json:"digest"` + Deleted bool `json:"deleted"` +} + +func init() { + err := regadapter.RegisterFactory(model.RegistryArtifactListExport, &factory{}) + if err != nil { + return + } +} + +type factory struct { +} + +// Create ... +func (f *factory) Create(r *model.Registry) (adp.Adapter, error) { + return newAdapter(r) +} + +// AdapterPattern ... +func (f *factory) AdapterPattern() *model.AdapterPattern { + return nil +} + +type adapter struct { +} + +func (a adapter) Info() (*model.RegistryInfo, error) { + return &model.RegistryInfo{}, nil +} + +func (a adapter) PrepareForPush(resources []*model.Resource) error { + + var ( + artifacts []Artifact + registry *model.Registry + ) + + for _, r := range resources { + if r.Metadata == nil { + continue + } + if r.Metadata.Repository == nil { + continue + } + if r.Registry == nil { + continue + } + + if registry == nil { + registry = r.Registry + } + + for _, at := range r.Metadata.Artifacts { + + artifacts = append(artifacts, Artifact{ + Repository: r.Metadata.Repository.Name, + Deleted: r.Deleted, + Tags: at.Tags, + Type: at.Type, + Digest: at.Digest, + }) + } + } + + if registry == nil { + return fmt.Errorf("no registry information found") + } + + result := &Result{ + Registry: registry.Name, + Artifacts: artifacts, + } + + data, err := json.Marshal(result) + if err != nil { + return errors.Wrap(err, "failed to marshal result") + } + + responseBody := bytes.NewBuffer(data) + resp, err := http.Post(registry.URL, "application/json", responseBody) + if err != nil { + return errors.Wrap(err, "failed to post result") + } + defer resp.Body.Close() + + return nil +} + +func (a adapter) HealthCheck() (string, error) { + return model.Healthy, nil +} + +func (a adapter) FetchArtifacts(filters []*model.Filter) ([]*model.Resource, error) { + return nil, nil +} + +func (a adapter) ManifestExist(repository, reference string) (exist bool, desc *distribution.Descriptor, err error) { + return true, nil, nil +} + +func (a adapter) PullManifest(repository, reference string, accepttedMediaTypes ...string) (manifest distribution.Manifest, digest string, err error) { + return nil, "", ErrNotImplemented +} + +func (a adapter) PushManifest(repository, reference, mediaType string, payload []byte) (string, error) { + //fmt.Println("push manifest", repository, reference) + return "", nil +} + +func (a adapter) DeleteManifest(repository, reference string) error { + return ErrNotImplemented +} + +func (a adapter) BlobExist(repository, digest string) (exist bool, err error) { + return true, nil +} + +func (a adapter) PullBlob(repository, digest string) (size int64, blob io.ReadCloser, err error) { + return 0, nil, ErrNotImplemented +} + +func (a adapter) PullBlobChunk(repository, digest string, blobSize, start, end int64) (size int64, blob io.ReadCloser, err error) { + return 0, nil, ErrNotImplemented +} + +func (a adapter) PushBlobChunk(repository, digest string, size int64, chunk io.Reader, start, end int64, location string) (nextUploadLocation string, endRange int64, err error) { + return "", 0, ErrNotImplemented +} + +func (a adapter) PushBlob(repository, digest string, size int64, blob io.Reader) error { + return nil +} + +func (a adapter) MountBlob(srcRepository, digest, dstRepository string) (err error) { + return nil +} + +func (a adapter) CanBeMount(digest string) (mount bool, repository string, err error) { + return false, "", ErrNotImplemented +} + +func (a adapter) DeleteTag(repository, tag string) error { + return ErrNotImplemented +} + +func (a adapter) ListTags(repository string) (tags []string, err error) { + return nil, nil +} + +func newAdapter(registry *model.Registry) (adp.Adapter, error) { + return &adapter{}, nil +} diff --git a/src/pkg/reg/manager.go b/src/pkg/reg/manager.go index c1d37671d90..bbdcc2e0fd2 100644 --- a/src/pkg/reg/manager.go +++ b/src/pkg/reg/manager.go @@ -55,6 +55,8 @@ import ( _ "github.com/goharbor/harbor/src/pkg/reg/adapter/tencentcr" // register the VolcEngine CR Registry adapter _ "github.com/goharbor/harbor/src/pkg/reg/adapter/volcenginecr" + // register artifact list exporter + _ "github.com/goharbor/harbor/src/pkg/reg/adapter/list-export" ) var ( diff --git a/src/pkg/reg/model/registry.go b/src/pkg/reg/model/registry.go index 1b4f945d713..cad1dd798eb 100644 --- a/src/pkg/reg/model/registry.go +++ b/src/pkg/reg/model/registry.go @@ -36,8 +36,9 @@ const ( RegistryTypeGithubCR = "github-ghcr" RegistryTypeVolcCR = "volcengine-cr" - RegistryTypeHelmHub = "helm-hub" - RegistryTypeArtifactHub = "artifact-hub" + RegistryTypeHelmHub = "helm-hub" + RegistryTypeArtifactHub = "artifact-hub" + RegistryArtifactListExport = "artifact-list-export" FilterStyleTypeText = "input" FilterStyleTypeRadio = "radio" From 8c0e3aa18078d6f23f5943b0f54a09a3d88de60e Mon Sep 17 00:00:00 2001 From: Maksym Trofimenko Date: Sun, 29 Sep 2024 21:21:02 +0100 Subject: [PATCH 2/9] adds pushing state image with list of artifacts --- src/pkg/reg/adapter/list-export/adapter.go | 76 +++++++++++++++++++--- 1 file changed, 66 insertions(+), 10 deletions(-) diff --git a/src/pkg/reg/adapter/list-export/adapter.go b/src/pkg/reg/adapter/list-export/adapter.go index 0d03ca619bb..02fcca7c465 100644 --- a/src/pkg/reg/adapter/list-export/adapter.go +++ b/src/pkg/reg/adapter/list-export/adapter.go @@ -5,17 +5,22 @@ import ( "encoding/json" "fmt" "github.com/docker/distribution" + "github.com/goharbor/harbor/src/common/secret" + "github.com/goharbor/harbor/src/lib/config" "github.com/goharbor/harbor/src/lib/errors" - adp "github.com/goharbor/harbor/src/pkg/reg/adapter" regadapter "github.com/goharbor/harbor/src/pkg/reg/adapter" "github.com/goharbor/harbor/src/pkg/reg/model" + "github.com/google/go-containerregistry/pkg/crane" "io" "net/http" + "net/url" + "path" + "time" ) var ( - _ adp.Adapter = (*adapter)(nil) - _ adp.ArtifactRegistry = (*adapter)(nil) + _ regadapter.Adapter = (*adapter)(nil) + _ regadapter.ArtifactRegistry = (*adapter)(nil) ) var ErrNotImplemented = errors.New("not implemented") @@ -27,6 +32,7 @@ type Result struct { type Artifact struct { Repository string `json:"repository"` Tags []string `json:"tag"` + Labels []string `json:"labels"` Type string `json:"type"` Digest string `json:"digest"` Deleted bool `json:"deleted"` @@ -43,7 +49,7 @@ type factory struct { } // Create ... -func (f *factory) Create(r *model.Registry) (adp.Adapter, error) { +func (f *factory) Create(r *model.Registry) (regadapter.Adapter, error) { return newAdapter(r) } @@ -53,6 +59,25 @@ func (f *factory) AdapterPattern() *model.AdapterPattern { } type adapter struct { + httpClient *http.Client +} + +func (a adapter) RoundTrip(request *http.Request) (*http.Response, error) { + + u, err := url.Parse(config.InternalCoreURL()) + if err != nil { + return nil, fmt.Errorf("unable to parse internal core url: %v", err) + } + + // replace request's host with core's address + request.Host = config.InternalCoreURL() + request.URL.Host = u.Host + + request.URL.Scheme = u.Scheme + // adds auth headers + _ = secret.AddToRequest(request, config.JobserviceSecret()) + + return a.httpClient.Do(request) } func (a adapter) Info() (*model.RegistryInfo, error) { @@ -62,8 +87,9 @@ func (a adapter) Info() (*model.RegistryInfo, error) { func (a adapter) PrepareForPush(resources []*model.Resource) error { var ( - artifacts []Artifact - registry *model.Registry + artifacts []Artifact + registry *model.Registry + destinationRepo string ) for _, r := range resources { @@ -82,11 +108,14 @@ func (a adapter) PrepareForPush(resources []*model.Resource) error { } for _, at := range r.Metadata.Artifacts { - + if destinationRepo == "" { + destinationRepo = r.Metadata.Repository.Name + } artifacts = append(artifacts, Artifact{ Repository: r.Metadata.Repository.Name, Deleted: r.Deleted, Tags: at.Tags, + Labels: at.Labels, Type: at.Type, Digest: at.Digest, }) @@ -107,6 +136,31 @@ func (a adapter) PrepareForPush(resources []*model.Resource) error { return errors.Wrap(err, "failed to marshal result") } + img, err := crane.Image(map[string][]byte{ + "artifacts.json": data, + }) + if err != nil { + return fmt.Errorf("image create failed: %v", err) + } + + destinationRepo = fmt.Sprintf("%s/%s", path.Dir(destinationRepo), "state") + // + + err = crane.Push(img, destinationRepo, crane.WithTransport(a)) + if err != nil { + return fmt.Errorf("push image failed: %v", err) + } + + err = crane.Tag(destinationRepo, fmt.Sprintf("%d", time.Now().Unix()), crane.WithTransport(a)) + if err != nil { + return fmt.Errorf("tag image failed: %v", err) + } + + err = crane.Tag(destinationRepo, "latest", crane.WithTransport(a)) + if err != nil { + return fmt.Errorf("tag image failed: %v", err) + } + responseBody := bytes.NewBuffer(data) resp, err := http.Post(registry.URL, "application/json", responseBody) if err != nil { @@ -134,7 +188,6 @@ func (a adapter) PullManifest(repository, reference string, accepttedMediaTypes } func (a adapter) PushManifest(repository, reference, mediaType string, payload []byte) (string, error) { - //fmt.Println("push manifest", repository, reference) return "", nil } @@ -178,6 +231,9 @@ func (a adapter) ListTags(repository string) (tags []string, err error) { return nil, nil } -func newAdapter(registry *model.Registry) (adp.Adapter, error) { - return &adapter{}, nil +func newAdapter(_ *model.Registry) (regadapter.Adapter, error) { + + return &adapter{ + httpClient: &http.Client{}, + }, nil } From ba3d74edf7cb320bf606b512c21218ffa7c877b4 Mon Sep 17 00:00:00 2001 From: Mehul-Kumar-27 <202151092@iiitvadodara.ac.in> Date: Tue, 15 Oct 2024 04:22:13 +0530 Subject: [PATCH 3/9] adds registry URL instead of name in state artifact --- src/pkg/reg/adapter/list-export/adapter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pkg/reg/adapter/list-export/adapter.go b/src/pkg/reg/adapter/list-export/adapter.go index 02fcca7c465..7741e086c71 100644 --- a/src/pkg/reg/adapter/list-export/adapter.go +++ b/src/pkg/reg/adapter/list-export/adapter.go @@ -127,7 +127,7 @@ func (a adapter) PrepareForPush(resources []*model.Resource) error { } result := &Result{ - Registry: registry.Name, + Registry: registry.URL, Artifacts: artifacts, } From 2a941b379babc6b60670cdd039c84ea645d78b5a Mon Sep 17 00:00:00 2001 From: Maksym Trofimenko Date: Sun, 22 Sep 2024 18:56:04 +0100 Subject: [PATCH 4/9] initial commit adds adapter --- .../job/impl/replication/replication.go | 2 + src/pkg/reg/adapter/list-export/adapter.go | 183 ++++++++++++++++++ src/pkg/reg/manager.go | 2 + src/pkg/reg/model/registry.go | 5 +- 4 files changed, 190 insertions(+), 2 deletions(-) create mode 100644 src/pkg/reg/adapter/list-export/adapter.go diff --git a/src/jobservice/job/impl/replication/replication.go b/src/jobservice/job/impl/replication/replication.go index 453fa5e032b..058d97fb832 100644 --- a/src/jobservice/job/impl/replication/replication.go +++ b/src/jobservice/job/impl/replication/replication.go @@ -53,6 +53,8 @@ import ( _ "github.com/goharbor/harbor/src/pkg/reg/adapter/tencentcr" // register the VolcEngine CR Registry adapter _ "github.com/goharbor/harbor/src/pkg/reg/adapter/volcenginecr" + // artifact list exporter + _ "github.com/goharbor/harbor/src/pkg/reg/adapter/list-export" "github.com/goharbor/harbor/src/pkg/reg/model" ) diff --git a/src/pkg/reg/adapter/list-export/adapter.go b/src/pkg/reg/adapter/list-export/adapter.go new file mode 100644 index 00000000000..0d03ca619bb --- /dev/null +++ b/src/pkg/reg/adapter/list-export/adapter.go @@ -0,0 +1,183 @@ +package list_export + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/docker/distribution" + "github.com/goharbor/harbor/src/lib/errors" + adp "github.com/goharbor/harbor/src/pkg/reg/adapter" + regadapter "github.com/goharbor/harbor/src/pkg/reg/adapter" + "github.com/goharbor/harbor/src/pkg/reg/model" + "io" + "net/http" +) + +var ( + _ adp.Adapter = (*adapter)(nil) + _ adp.ArtifactRegistry = (*adapter)(nil) +) +var ErrNotImplemented = errors.New("not implemented") + +type Result struct { + Registry string `json:"registry"` + Artifacts []Artifact `json:"artifacts"` +} + +type Artifact struct { + Repository string `json:"repository"` + Tags []string `json:"tag"` + Type string `json:"type"` + Digest string `json:"digest"` + Deleted bool `json:"deleted"` +} + +func init() { + err := regadapter.RegisterFactory(model.RegistryArtifactListExport, &factory{}) + if err != nil { + return + } +} + +type factory struct { +} + +// Create ... +func (f *factory) Create(r *model.Registry) (adp.Adapter, error) { + return newAdapter(r) +} + +// AdapterPattern ... +func (f *factory) AdapterPattern() *model.AdapterPattern { + return nil +} + +type adapter struct { +} + +func (a adapter) Info() (*model.RegistryInfo, error) { + return &model.RegistryInfo{}, nil +} + +func (a adapter) PrepareForPush(resources []*model.Resource) error { + + var ( + artifacts []Artifact + registry *model.Registry + ) + + for _, r := range resources { + if r.Metadata == nil { + continue + } + if r.Metadata.Repository == nil { + continue + } + if r.Registry == nil { + continue + } + + if registry == nil { + registry = r.Registry + } + + for _, at := range r.Metadata.Artifacts { + + artifacts = append(artifacts, Artifact{ + Repository: r.Metadata.Repository.Name, + Deleted: r.Deleted, + Tags: at.Tags, + Type: at.Type, + Digest: at.Digest, + }) + } + } + + if registry == nil { + return fmt.Errorf("no registry information found") + } + + result := &Result{ + Registry: registry.Name, + Artifacts: artifacts, + } + + data, err := json.Marshal(result) + if err != nil { + return errors.Wrap(err, "failed to marshal result") + } + + responseBody := bytes.NewBuffer(data) + resp, err := http.Post(registry.URL, "application/json", responseBody) + if err != nil { + return errors.Wrap(err, "failed to post result") + } + defer resp.Body.Close() + + return nil +} + +func (a adapter) HealthCheck() (string, error) { + return model.Healthy, nil +} + +func (a adapter) FetchArtifacts(filters []*model.Filter) ([]*model.Resource, error) { + return nil, nil +} + +func (a adapter) ManifestExist(repository, reference string) (exist bool, desc *distribution.Descriptor, err error) { + return true, nil, nil +} + +func (a adapter) PullManifest(repository, reference string, accepttedMediaTypes ...string) (manifest distribution.Manifest, digest string, err error) { + return nil, "", ErrNotImplemented +} + +func (a adapter) PushManifest(repository, reference, mediaType string, payload []byte) (string, error) { + //fmt.Println("push manifest", repository, reference) + return "", nil +} + +func (a adapter) DeleteManifest(repository, reference string) error { + return ErrNotImplemented +} + +func (a adapter) BlobExist(repository, digest string) (exist bool, err error) { + return true, nil +} + +func (a adapter) PullBlob(repository, digest string) (size int64, blob io.ReadCloser, err error) { + return 0, nil, ErrNotImplemented +} + +func (a adapter) PullBlobChunk(repository, digest string, blobSize, start, end int64) (size int64, blob io.ReadCloser, err error) { + return 0, nil, ErrNotImplemented +} + +func (a adapter) PushBlobChunk(repository, digest string, size int64, chunk io.Reader, start, end int64, location string) (nextUploadLocation string, endRange int64, err error) { + return "", 0, ErrNotImplemented +} + +func (a adapter) PushBlob(repository, digest string, size int64, blob io.Reader) error { + return nil +} + +func (a adapter) MountBlob(srcRepository, digest, dstRepository string) (err error) { + return nil +} + +func (a adapter) CanBeMount(digest string) (mount bool, repository string, err error) { + return false, "", ErrNotImplemented +} + +func (a adapter) DeleteTag(repository, tag string) error { + return ErrNotImplemented +} + +func (a adapter) ListTags(repository string) (tags []string, err error) { + return nil, nil +} + +func newAdapter(registry *model.Registry) (adp.Adapter, error) { + return &adapter{}, nil +} diff --git a/src/pkg/reg/manager.go b/src/pkg/reg/manager.go index c1d37671d90..bbdcc2e0fd2 100644 --- a/src/pkg/reg/manager.go +++ b/src/pkg/reg/manager.go @@ -55,6 +55,8 @@ import ( _ "github.com/goharbor/harbor/src/pkg/reg/adapter/tencentcr" // register the VolcEngine CR Registry adapter _ "github.com/goharbor/harbor/src/pkg/reg/adapter/volcenginecr" + // register artifact list exporter + _ "github.com/goharbor/harbor/src/pkg/reg/adapter/list-export" ) var ( diff --git a/src/pkg/reg/model/registry.go b/src/pkg/reg/model/registry.go index 1b4f945d713..cad1dd798eb 100644 --- a/src/pkg/reg/model/registry.go +++ b/src/pkg/reg/model/registry.go @@ -36,8 +36,9 @@ const ( RegistryTypeGithubCR = "github-ghcr" RegistryTypeVolcCR = "volcengine-cr" - RegistryTypeHelmHub = "helm-hub" - RegistryTypeArtifactHub = "artifact-hub" + RegistryTypeHelmHub = "helm-hub" + RegistryTypeArtifactHub = "artifact-hub" + RegistryArtifactListExport = "artifact-list-export" FilterStyleTypeText = "input" FilterStyleTypeRadio = "radio" From 03c441db16a1bc4267b472c48660335ec20e7314 Mon Sep 17 00:00:00 2001 From: Maksym Trofimenko Date: Sun, 29 Sep 2024 21:21:02 +0100 Subject: [PATCH 5/9] adds pushing state image with list of artifacts --- src/pkg/reg/adapter/list-export/adapter.go | 76 +++++++++++++++++++--- 1 file changed, 66 insertions(+), 10 deletions(-) diff --git a/src/pkg/reg/adapter/list-export/adapter.go b/src/pkg/reg/adapter/list-export/adapter.go index 0d03ca619bb..02fcca7c465 100644 --- a/src/pkg/reg/adapter/list-export/adapter.go +++ b/src/pkg/reg/adapter/list-export/adapter.go @@ -5,17 +5,22 @@ import ( "encoding/json" "fmt" "github.com/docker/distribution" + "github.com/goharbor/harbor/src/common/secret" + "github.com/goharbor/harbor/src/lib/config" "github.com/goharbor/harbor/src/lib/errors" - adp "github.com/goharbor/harbor/src/pkg/reg/adapter" regadapter "github.com/goharbor/harbor/src/pkg/reg/adapter" "github.com/goharbor/harbor/src/pkg/reg/model" + "github.com/google/go-containerregistry/pkg/crane" "io" "net/http" + "net/url" + "path" + "time" ) var ( - _ adp.Adapter = (*adapter)(nil) - _ adp.ArtifactRegistry = (*adapter)(nil) + _ regadapter.Adapter = (*adapter)(nil) + _ regadapter.ArtifactRegistry = (*adapter)(nil) ) var ErrNotImplemented = errors.New("not implemented") @@ -27,6 +32,7 @@ type Result struct { type Artifact struct { Repository string `json:"repository"` Tags []string `json:"tag"` + Labels []string `json:"labels"` Type string `json:"type"` Digest string `json:"digest"` Deleted bool `json:"deleted"` @@ -43,7 +49,7 @@ type factory struct { } // Create ... -func (f *factory) Create(r *model.Registry) (adp.Adapter, error) { +func (f *factory) Create(r *model.Registry) (regadapter.Adapter, error) { return newAdapter(r) } @@ -53,6 +59,25 @@ func (f *factory) AdapterPattern() *model.AdapterPattern { } type adapter struct { + httpClient *http.Client +} + +func (a adapter) RoundTrip(request *http.Request) (*http.Response, error) { + + u, err := url.Parse(config.InternalCoreURL()) + if err != nil { + return nil, fmt.Errorf("unable to parse internal core url: %v", err) + } + + // replace request's host with core's address + request.Host = config.InternalCoreURL() + request.URL.Host = u.Host + + request.URL.Scheme = u.Scheme + // adds auth headers + _ = secret.AddToRequest(request, config.JobserviceSecret()) + + return a.httpClient.Do(request) } func (a adapter) Info() (*model.RegistryInfo, error) { @@ -62,8 +87,9 @@ func (a adapter) Info() (*model.RegistryInfo, error) { func (a adapter) PrepareForPush(resources []*model.Resource) error { var ( - artifacts []Artifact - registry *model.Registry + artifacts []Artifact + registry *model.Registry + destinationRepo string ) for _, r := range resources { @@ -82,11 +108,14 @@ func (a adapter) PrepareForPush(resources []*model.Resource) error { } for _, at := range r.Metadata.Artifacts { - + if destinationRepo == "" { + destinationRepo = r.Metadata.Repository.Name + } artifacts = append(artifacts, Artifact{ Repository: r.Metadata.Repository.Name, Deleted: r.Deleted, Tags: at.Tags, + Labels: at.Labels, Type: at.Type, Digest: at.Digest, }) @@ -107,6 +136,31 @@ func (a adapter) PrepareForPush(resources []*model.Resource) error { return errors.Wrap(err, "failed to marshal result") } + img, err := crane.Image(map[string][]byte{ + "artifacts.json": data, + }) + if err != nil { + return fmt.Errorf("image create failed: %v", err) + } + + destinationRepo = fmt.Sprintf("%s/%s", path.Dir(destinationRepo), "state") + // + + err = crane.Push(img, destinationRepo, crane.WithTransport(a)) + if err != nil { + return fmt.Errorf("push image failed: %v", err) + } + + err = crane.Tag(destinationRepo, fmt.Sprintf("%d", time.Now().Unix()), crane.WithTransport(a)) + if err != nil { + return fmt.Errorf("tag image failed: %v", err) + } + + err = crane.Tag(destinationRepo, "latest", crane.WithTransport(a)) + if err != nil { + return fmt.Errorf("tag image failed: %v", err) + } + responseBody := bytes.NewBuffer(data) resp, err := http.Post(registry.URL, "application/json", responseBody) if err != nil { @@ -134,7 +188,6 @@ func (a adapter) PullManifest(repository, reference string, accepttedMediaTypes } func (a adapter) PushManifest(repository, reference, mediaType string, payload []byte) (string, error) { - //fmt.Println("push manifest", repository, reference) return "", nil } @@ -178,6 +231,9 @@ func (a adapter) ListTags(repository string) (tags []string, err error) { return nil, nil } -func newAdapter(registry *model.Registry) (adp.Adapter, error) { - return &adapter{}, nil +func newAdapter(_ *model.Registry) (regadapter.Adapter, error) { + + return &adapter{ + httpClient: &http.Client{}, + }, nil } From 21ea9630e57e555b80b6d5d3fc32de64cdd8d6b8 Mon Sep 17 00:00:00 2001 From: Mehul-Kumar-27 <202151092@iiitvadodara.ac.in> Date: Tue, 15 Oct 2024 04:22:13 +0530 Subject: [PATCH 6/9] adds registry URL instead of name in state artifact --- src/pkg/reg/adapter/list-export/adapter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pkg/reg/adapter/list-export/adapter.go b/src/pkg/reg/adapter/list-export/adapter.go index 02fcca7c465..7741e086c71 100644 --- a/src/pkg/reg/adapter/list-export/adapter.go +++ b/src/pkg/reg/adapter/list-export/adapter.go @@ -127,7 +127,7 @@ func (a adapter) PrepareForPush(resources []*model.Resource) error { } result := &Result{ - Registry: registry.Name, + Registry: registry.URL, Artifacts: artifacts, } From c4292b5c1cd57f73a22a40585917410ff461f80f Mon Sep 17 00:00:00 2001 From: Vadim Bauer Date: Tue, 15 Oct 2024 16:17:54 +0200 Subject: [PATCH 7/9] Merge pull request #44 from Mehul-Kumar-27/state-artifact-fix Adds the registry URL to the state artifact file instead of its name From 58a160d2187ca539203b94084308e47801a45933 Mon Sep 17 00:00:00 2001 From: bupd Date: Mon, 21 Oct 2024 14:23:24 +0530 Subject: [PATCH 8/9] update adapter to send list to ground control Signed-off-by: bupd --- src/controller/replication/flow/stage.go | 42 +++++++++-- src/pkg/reg/adapter/list-export/adapter.go | 87 ++++++++++++---------- 2 files changed, 82 insertions(+), 47 deletions(-) diff --git a/src/controller/replication/flow/stage.go b/src/controller/replication/flow/stage.go index c2ad07d72c8..83afcd2e19b 100644 --- a/src/controller/replication/flow/stage.go +++ b/src/controller/replication/flow/stage.go @@ -73,7 +73,8 @@ func fetchResources(adapter adp.Adapter, policy *repctlmodel.Policy) ([]*model.R // assemble the source resources by filling the registry information func assembleSourceResources(resources []*model.Resource, - policy *repctlmodel.Policy) []*model.Resource { + policy *repctlmodel.Policy, +) []*model.Resource { for _, resource := range resources { resource.Registry = policy.SrcRegistry } @@ -83,25 +84,51 @@ func assembleSourceResources(resources []*model.Resource, // assemble the destination resources by filling the metadata, registry and override properties func assembleDestinationResources(resources []*model.Resource, - policy *repctlmodel.Policy, dstRepoComponentPathType string) ([]*model.Resource, error) { + policy *repctlmodel.Policy, dstRepoComponentPathType string, +) ([]*model.Resource, error) { var result []*model.Resource for _, resource := range resources { - name, err := replaceNamespace(resource.Metadata.Repository.Name, policy.DestNamespace, policy.DestNamespaceReplaceCount, dstRepoComponentPathType) - if err != nil { - return nil, err + var registry *model.Registry + var repositoryName string + var err error + + log.Debugf("assembling dest resources...") + + // Check condition to determine whether to assemble list or destination resources + if policy.DestRegistry.Type == "artifact-list-export" { + // Assemble list resources + registry = policy.SrcRegistry + repositoryName = resource.Metadata.Repository.Name + + if resource.ExtendedInfo == nil { + resource.ExtendedInfo = make(map[string]interface{}) + } + resource.ExtendedInfo["destinationURL"] = policy.DestRegistry.URL + resource.ExtendedInfo["groupName"] = policy.DestNamespace + } else { + // Assemble destination resources + registry = policy.DestRegistry + repositoryName, err = replaceNamespace(resource.Metadata.Repository.Name, policy.DestNamespace, policy.DestNamespaceReplaceCount, dstRepoComponentPathType) + if err != nil { + return nil, err + } } + + // Create the resource res := &model.Resource{ Type: resource.Type, - Registry: policy.DestRegistry, + Registry: registry, ExtendedInfo: resource.ExtendedInfo, Deleted: resource.Deleted, IsDeleteTag: resource.IsDeleteTag, Override: policy.Override, Skip: resource.Skip, } + + // Fill the resource metadata res.Metadata = &model.ResourceMetadata{ Repository: &model.Repository{ - Name: name, + Name: repositoryName, Metadata: resource.Metadata.Repository.Metadata, }, Vtags: resource.Metadata.Vtags, @@ -109,6 +136,7 @@ func assembleDestinationResources(resources []*model.Resource, } result = append(result, res) } + log.Debug("assemble the destination resources completed") return result, nil } diff --git a/src/pkg/reg/adapter/list-export/adapter.go b/src/pkg/reg/adapter/list-export/adapter.go index 7741e086c71..0843be6b0b2 100644 --- a/src/pkg/reg/adapter/list-export/adapter.go +++ b/src/pkg/reg/adapter/list-export/adapter.go @@ -4,18 +4,16 @@ import ( "bytes" "encoding/json" "fmt" + "io" + "net/http" + "net/url" + "github.com/docker/distribution" "github.com/goharbor/harbor/src/common/secret" "github.com/goharbor/harbor/src/lib/config" "github.com/goharbor/harbor/src/lib/errors" regadapter "github.com/goharbor/harbor/src/pkg/reg/adapter" "github.com/goharbor/harbor/src/pkg/reg/model" - "github.com/google/go-containerregistry/pkg/crane" - "io" - "net/http" - "net/url" - "path" - "time" ) var ( @@ -25,6 +23,7 @@ var ( var ErrNotImplemented = errors.New("not implemented") type Result struct { + Group string `json:"group"` Registry string `json:"registry"` Artifacts []Artifact `json:"artifacts"` } @@ -45,8 +44,7 @@ func init() { } } -type factory struct { -} +type factory struct{} // Create ... func (f *factory) Create(r *model.Registry) (regadapter.Adapter, error) { @@ -63,7 +61,6 @@ type adapter struct { } func (a adapter) RoundTrip(request *http.Request) (*http.Response, error) { - u, err := url.Parse(config.InternalCoreURL()) if err != nil { return nil, fmt.Errorf("unable to parse internal core url: %v", err) @@ -85,11 +82,11 @@ func (a adapter) Info() (*model.RegistryInfo, error) { } func (a adapter) PrepareForPush(resources []*model.Resource) error { - var ( - artifacts []Artifact - registry *model.Registry - destinationRepo string + artifacts []Artifact + registry *model.Registry + destinationURL string + groupName string ) for _, r := range resources { @@ -102,15 +99,32 @@ func (a adapter) PrepareForPush(resources []*model.Resource) error { if r.Registry == nil { continue } + if r.ExtendedInfo == nil { + return fmt.Errorf("extended_info map is nil") + } if registry == nil { registry = r.Registry } + if destinationURL == "" { + destURL, ok := r.ExtendedInfo["destinationURL"].(string) + if ok { + destinationURL = destURL + } else { + return fmt.Errorf("destination_url not a string or missing") + } + } - for _, at := range r.Metadata.Artifacts { - if destinationRepo == "" { - destinationRepo = r.Metadata.Repository.Name + if groupName == "" { + grp, ok := r.ExtendedInfo["groupName"].(string) + if ok { + groupName = grp + } else { + return fmt.Errorf("groupName not a string or missing") } + } + + for _, at := range r.Metadata.Artifacts { artifacts = append(artifacts, Artifact{ Repository: r.Metadata.Repository.Name, Deleted: r.Deleted, @@ -127,6 +141,7 @@ func (a adapter) PrepareForPush(resources []*model.Resource) error { } result := &Result{ + Group: groupName, Registry: registry.URL, Artifacts: artifacts, } @@ -136,38 +151,31 @@ func (a adapter) PrepareForPush(resources []*model.Resource) error { return errors.Wrap(err, "failed to marshal result") } - img, err := crane.Image(map[string][]byte{ - "artifacts.json": data, - }) - if err != nil { - return fmt.Errorf("image create failed: %v", err) - } - - destinationRepo = fmt.Sprintf("%s/%s", path.Dir(destinationRepo), "state") - // + fmt.Println("kumar is here \n \n\n kumar") + fmt.Printf("json %v: \n \n\n kumar", string(data)) - err = crane.Push(img, destinationRepo, crane.WithTransport(a)) + // Create a POST request + req, err := http.NewRequest("POST", destinationURL, bytes.NewBuffer(data)) if err != nil { - return fmt.Errorf("push image failed: %v", err) + fmt.Println("Error creating request:", err) + return nil } - err = crane.Tag(destinationRepo, fmt.Sprintf("%d", time.Now().Unix()), crane.WithTransport(a)) - if err != nil { - return fmt.Errorf("tag image failed: %v", err) - } - - err = crane.Tag(destinationRepo, "latest", crane.WithTransport(a)) - if err != nil { - return fmt.Errorf("tag image failed: %v", err) - } + // Set the content type header + req.Header.Set("Content-Type", "application/json") - responseBody := bytes.NewBuffer(data) - resp, err := http.Post(registry.URL, "application/json", responseBody) + // Send the request using http.Client + client := &http.Client{} + resp, err := client.Do(req) if err != nil { - return errors.Wrap(err, "failed to post result") + fmt.Println("Error sending request:", err) + return nil } defer resp.Body.Close() + // Print the response status + fmt.Println("Response Status:", resp.Status) + return nil } @@ -232,7 +240,6 @@ func (a adapter) ListTags(repository string) (tags []string, err error) { } func newAdapter(_ *model.Registry) (regadapter.Adapter, error) { - return &adapter{ httpClient: &http.Client{}, }, nil From 41cdbc239dbae6d579b42909ae42582bdbe98b2c Mon Sep 17 00:00:00 2001 From: bupd Date: Mon, 2 Dec 2024 17:41:28 +0530 Subject: [PATCH 9/9] update list-export to harbor-satellite Signed-off-by: bupd --- src/controller/replication/flow/stage.go | 2 +- .../job/impl/replication/replication.go | 4 ++-- .../{list-export => harborsatellite}/adapter.go | 16 ++++------------ src/pkg/reg/manager.go | 4 ++-- src/pkg/reg/model/registry.go | 6 +++--- 5 files changed, 12 insertions(+), 20 deletions(-) rename src/pkg/reg/adapter/{list-export => harborsatellite}/adapter.go (93%) diff --git a/src/controller/replication/flow/stage.go b/src/controller/replication/flow/stage.go index 83afcd2e19b..290f42291de 100644 --- a/src/controller/replication/flow/stage.go +++ b/src/controller/replication/flow/stage.go @@ -95,7 +95,7 @@ func assembleDestinationResources(resources []*model.Resource, log.Debugf("assembling dest resources...") // Check condition to determine whether to assemble list or destination resources - if policy.DestRegistry.Type == "artifact-list-export" { + if policy.DestRegistry.Type == "harbor-satellite" { // Assemble list resources registry = policy.SrcRegistry repositoryName = resource.Metadata.Repository.Name diff --git a/src/jobservice/job/impl/replication/replication.go b/src/jobservice/job/impl/replication/replication.go index 058d97fb832..f25dbb33785 100644 --- a/src/jobservice/job/impl/replication/replication.go +++ b/src/jobservice/job/impl/replication/replication.go @@ -41,6 +41,8 @@ import ( _ "github.com/goharbor/harbor/src/pkg/reg/adapter/googlegcr" // import harbor adapter _ "github.com/goharbor/harbor/src/pkg/reg/adapter/harbor" + // import harborSatellite adapter + _ "github.com/goharbor/harbor/src/pkg/reg/adapter/harborsatellite" // import huawei adapter _ "github.com/goharbor/harbor/src/pkg/reg/adapter/huawei" // import jfrog adapter @@ -53,8 +55,6 @@ import ( _ "github.com/goharbor/harbor/src/pkg/reg/adapter/tencentcr" // register the VolcEngine CR Registry adapter _ "github.com/goharbor/harbor/src/pkg/reg/adapter/volcenginecr" - // artifact list exporter - _ "github.com/goharbor/harbor/src/pkg/reg/adapter/list-export" "github.com/goharbor/harbor/src/pkg/reg/model" ) diff --git a/src/pkg/reg/adapter/list-export/adapter.go b/src/pkg/reg/adapter/harborsatellite/adapter.go similarity index 93% rename from src/pkg/reg/adapter/list-export/adapter.go rename to src/pkg/reg/adapter/harborsatellite/adapter.go index 0843be6b0b2..261a092104c 100644 --- a/src/pkg/reg/adapter/list-export/adapter.go +++ b/src/pkg/reg/adapter/harborsatellite/adapter.go @@ -1,4 +1,4 @@ -package list_export +package harborsatellite import ( "bytes" @@ -38,7 +38,7 @@ type Artifact struct { } func init() { - err := regadapter.RegisterFactory(model.RegistryArtifactListExport, &factory{}) + err := regadapter.RegisterFactory(model.RegistryHarborSatellite, &factory{}) if err != nil { return } @@ -151,14 +151,10 @@ func (a adapter) PrepareForPush(resources []*model.Resource) error { return errors.Wrap(err, "failed to marshal result") } - fmt.Println("kumar is here \n \n\n kumar") - fmt.Printf("json %v: \n \n\n kumar", string(data)) - // Create a POST request req, err := http.NewRequest("POST", destinationURL, bytes.NewBuffer(data)) if err != nil { - fmt.Println("Error creating request:", err) - return nil + return fmt.Errorf("error creating request: %v", err) } // Set the content type header @@ -168,14 +164,10 @@ func (a adapter) PrepareForPush(resources []*model.Resource) error { client := &http.Client{} resp, err := client.Do(req) if err != nil { - fmt.Println("Error sending request:", err) - return nil + return fmt.Errorf("error sending request: %v", err) } defer resp.Body.Close() - // Print the response status - fmt.Println("Response Status:", resp.Status) - return nil } diff --git a/src/pkg/reg/manager.go b/src/pkg/reg/manager.go index bbdcc2e0fd2..83aaea6aeb3 100644 --- a/src/pkg/reg/manager.go +++ b/src/pkg/reg/manager.go @@ -43,6 +43,8 @@ import ( _ "github.com/goharbor/harbor/src/pkg/reg/adapter/googlegcr" // register the Harbor adapter _ "github.com/goharbor/harbor/src/pkg/reg/adapter/harbor" + // register the HarborSatellite adapter + _ "github.com/goharbor/harbor/src/pkg/reg/adapter/harborsatellite" // register the huawei adapter _ "github.com/goharbor/harbor/src/pkg/reg/adapter/huawei" // register the Jfrog Artifactory adapter @@ -55,8 +57,6 @@ import ( _ "github.com/goharbor/harbor/src/pkg/reg/adapter/tencentcr" // register the VolcEngine CR Registry adapter _ "github.com/goharbor/harbor/src/pkg/reg/adapter/volcenginecr" - // register artifact list exporter - _ "github.com/goharbor/harbor/src/pkg/reg/adapter/list-export" ) var ( diff --git a/src/pkg/reg/model/registry.go b/src/pkg/reg/model/registry.go index cad1dd798eb..ee3e03f8c71 100644 --- a/src/pkg/reg/model/registry.go +++ b/src/pkg/reg/model/registry.go @@ -36,9 +36,9 @@ const ( RegistryTypeGithubCR = "github-ghcr" RegistryTypeVolcCR = "volcengine-cr" - RegistryTypeHelmHub = "helm-hub" - RegistryTypeArtifactHub = "artifact-hub" - RegistryArtifactListExport = "artifact-list-export" + RegistryTypeHelmHub = "helm-hub" + RegistryTypeArtifactHub = "artifact-hub" + RegistryHarborSatellite = "harbor-satellite" FilterStyleTypeText = "input" FilterStyleTypeRadio = "radio"