Skip to content

Commit

Permalink
add harbor registry client
Browse files Browse the repository at this point in the history
  • Loading branch information
refaelm92 committed Nov 21, 2024
1 parent 6eefbf3 commit f8b7227
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 79 deletions.
86 changes: 86 additions & 0 deletions registryclients/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package registryclients

import (
"context"
"github.com/Masterminds/semver/v3"
"github.com/armosec/registryx/common"
"github.com/armosec/registryx/interfaces"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/v1/remote"
"slices"
"sort"
"strings"
)

const (
latestTag = "latest"
)

func getAllRepositories(ctx context.Context, registry interfaces.IRegistry) ([]string, error) {
var repos, pageRepos []string
var nextPage *common.PaginationOption
var err error

firstPage := common.MakePagination(registry.GetMaxPageSize())
catalogOpts := common.CatalogOption{}

for pageRepos, nextPage, err = registry.Catalog(ctx, firstPage, catalogOpts, authn.FromConfig(*registry.GetAuth())); ; pageRepos, nextPage, err = registry.Catalog(ctx, *nextPage, catalogOpts, authn.FromConfig(*registry.GetAuth())) {
if err != nil {
return nil, err
}
if len(pageRepos) == 0 {
break
}
repos = append(repos, pageRepos...)

if nextPage == nil || nextPage.Cursor == "" {
break
}
}
return repos, nil
}

func getImageLatestTag(repo string, registry interfaces.IRegistry) (string, error) {
firstPage := common.MakePagination(1000)
var tags []string
withAuth := remote.WithAuth(authn.FromConfig(*registry.GetAuth()))
if latestTags, err := registry.GetLatestTags(repo, 1, withAuth); err == nil {
for _, tag := range latestTags {
if strings.HasSuffix(tag, ".sig") {
continue
}
tagsForDigest := strings.Split(tag, ",")
return tagsForDigest[0], nil
}
} else {
for tagsPage, nextPage, err := registry.List(repo, firstPage, withAuth); ; tagsPage, nextPage, err = registry.List(repo, *nextPage, withAuth) {
if err != nil {
return "", err
}

if slices.Contains(tagsPage, latestTag) {
return latestTag, nil
}

tags = append(tags, tagsPage...)

if nextPage == nil {
break
}
}
return getLatestTag(tags), nil
}
return "", nil
}

func getLatestTag(tags []string) string {
var versions []*semver.Version
for _, tag := range tags {
version, err := semver.NewVersion(tag)
if err == nil {
versions = append(versions, version)
}
}
sort.Sort(sort.Reverse(semver.Collection(versions)))
return versions[0].String()
}
59 changes: 59 additions & 0 deletions registryclients/harbor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package registryclients

import (
"context"
"fmt"
"github.com/armosec/armoapi-go/armotypes"
"github.com/armosec/registryx/common"
"github.com/armosec/registryx/registries/harbor"
dockerregistry "github.com/docker/docker/api/types/registry"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
)

type HarborRegistryClient struct {
Registry *armotypes.HarborImageRegistry
}

func (h *HarborRegistryClient) GetAllRepositories(ctx context.Context) ([]string, error) {
registry, err := name.NewRegistry(h.Registry.InstanceURL)
if err != nil {
return nil, err
}
iRegistry, err := harbor.NewHarborRegistry(&authn.AuthConfig{Username: h.Registry.Username, Password: h.Registry.Password}, &registry, &common.RegistryOptions{})
if err != nil {
return nil, err
}

return getAllRepositories(ctx, iRegistry)
}

func (h *HarborRegistryClient) GetImagesToScan(_ context.Context) (map[string]string, error) {
registry, err := name.NewRegistry(h.Registry.InstanceURL)
if err != nil {
return nil, err
}
iRegistry, err := harbor.NewHarborRegistry(&authn.AuthConfig{Username: h.Registry.Username, Password: h.Registry.Password}, &registry, &common.RegistryOptions{})
if err != nil {
return nil, err
}

images := make(map[string]string, len(h.Registry.Repositories))
for _, repository := range h.Registry.Repositories {
tag, err := getImageLatestTag(repository, iRegistry)
if err != nil {
return nil, err
} else if tag == "" {
return nil, fmt.Errorf("failed to find latest tag for repository %s", repository)
}
images[fmt.Sprintf("%s/%s", h.Registry.InstanceURL, repository)] = tag
}
return images, nil
}

func (h *HarborRegistryClient) GetDockerAuth() *dockerregistry.AuthConfig {
return &dockerregistry.AuthConfig{
Username: h.Registry.Username,
Password: h.Registry.Password,
}
}
99 changes: 20 additions & 79 deletions registryclients/quay.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,55 +3,46 @@ package registryclients
import (
"context"
"fmt"
"github.com/Masterminds/semver/v3"
"github.com/armosec/armoapi-go/armotypes"
"github.com/armosec/registryx/common"
"github.com/armosec/registryx/registries"
"github.com/armosec/registryx/registries/quay"
dockerregistry "github.com/docker/docker/api/types/registry"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/v1/remote"
"slices"
"sort"
"strings"
"github.com/google/go-containerregistry/pkg/name"
)

type QuayRegistryClient struct {
Registry *armotypes.QuayImageRegistry
}

func (q *QuayRegistryClient) GetAllRepositories(ctx context.Context) ([]string, error) {
iRegistry, err := registries.Factory(&authn.AuthConfig{Username: q.Registry.RobotAccountName, Password: q.Registry.RobotAccountToken}, q.Registry.ContainerRegistryName, nil)
registry, err := name.NewRegistry(q.Registry.ContainerRegistryName)
if err != nil {
return nil, err
}
var repos, pageRepos []string
var nextPage *common.PaginationOption

firstPage := common.MakePagination(iRegistry.GetMaxPageSize())
catalogOpts := common.CatalogOption{}

for pageRepos, nextPage, err = iRegistry.Catalog(ctx, firstPage, catalogOpts, authn.FromConfig(*iRegistry.GetAuth())); ; pageRepos, nextPage, err = iRegistry.Catalog(ctx, *nextPage, catalogOpts, authn.FromConfig(*iRegistry.GetAuth())) {
if err != nil {
return nil, err
}
if len(pageRepos) == 0 {
break
}
repos = append(repos, pageRepos...)

if nextPage == nil || nextPage.Cursor == "" {
break
}
iRegistry, err := quay.NewQuayIORegistry(&authn.AuthConfig{Username: q.Registry.RobotAccountName, Password: q.Registry.RobotAccountToken}, &registry, nil)
if err != nil {
return nil, err
}
return repos, nil
return getAllRepositories(ctx, iRegistry)
}

func (q *QuayRegistryClient) GetImagesToScan(ctx context.Context) (map[string]string, error) {
func (q *QuayRegistryClient) GetImagesToScan(_ context.Context) (map[string]string, error) {
registry, err := name.NewRegistry(q.Registry.ContainerRegistryName)
if err != nil {
return nil, err
}
iRegistry, err := quay.NewQuayIORegistry(&authn.AuthConfig{Username: q.Registry.RobotAccountName, Password: q.Registry.RobotAccountToken}, &registry, nil)
if err != nil {
return nil, err
}

images := make(map[string]string, len(q.Registry.Repositories))
for _, repository := range q.Registry.Repositories {
tag, err := q.getImageLatestTag(ctx, repository)
tag, err := getImageLatestTag(repository, iRegistry)
if err != nil {
return nil, err
} else if tag == "" {
return nil, fmt.Errorf("failed to find latest tag for repository %s", repository)
}
images[fmt.Sprintf("%s/%s", q.Registry.ContainerRegistryName, repository)] = tag
}
Expand All @@ -64,53 +55,3 @@ func (q *QuayRegistryClient) GetDockerAuth() *dockerregistry.AuthConfig {
Password: q.Registry.RobotAccountToken,
}
}

func (q *QuayRegistryClient) getImageLatestTag(_ context.Context, repo string) (string, error) {
iRegistry, err := registries.Factory(&authn.AuthConfig{Username: q.Registry.RobotAccountName, Password: q.Registry.RobotAccountToken}, q.Registry.ContainerRegistryName, nil)
if err != nil {
return "", err
}

firstPage := common.MakePagination(1000)
var tags []string
options := []remote.Option{remote.WithAuth(authn.FromConfig(*iRegistry.GetAuth()))}
if latestTags, err := iRegistry.GetLatestTags(repo, 1, options...); err == nil {
for _, tag := range latestTags {
if strings.HasSuffix(tag, ".sig") {
continue
}
tagsForDigest := strings.Split(tag, ",")
return tagsForDigest[0], nil
}
} else {
for tagsPage, nextPage, err := iRegistry.List(repo, firstPage, options...); ; tagsPage, nextPage, err = iRegistry.List(repo, *nextPage) {
if err != nil {
return "", err
}

if slices.Contains(tagsPage, "latest") {
return "latest", nil
}

tags = append(tags, tagsPage...)

if nextPage == nil {
break
}
}
return getLatestTag(tags), nil
}
return "", nil
}

func getLatestTag(tags []string) string {
var versions []*semver.Version
for _, tag := range tags {
version, err := semver.NewVersion(tag)
if err == nil {
versions = append(versions, version)
}
}
sort.Sort(sort.Reverse(semver.Collection(versions)))
return versions[0].String()
}

0 comments on commit f8b7227

Please sign in to comment.