Skip to content

Commit 22c4b47

Browse files
authored
Merge pull request #12 from armosec/harbor
add harbor registry client
2 parents 6eefbf3 + f8b7227 commit 22c4b47

File tree

3 files changed

+165
-79
lines changed

3 files changed

+165
-79
lines changed

registryclients/common.go

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package registryclients
2+
3+
import (
4+
"context"
5+
"github.com/Masterminds/semver/v3"
6+
"github.com/armosec/registryx/common"
7+
"github.com/armosec/registryx/interfaces"
8+
"github.com/google/go-containerregistry/pkg/authn"
9+
"github.com/google/go-containerregistry/pkg/v1/remote"
10+
"slices"
11+
"sort"
12+
"strings"
13+
)
14+
15+
const (
16+
latestTag = "latest"
17+
)
18+
19+
func getAllRepositories(ctx context.Context, registry interfaces.IRegistry) ([]string, error) {
20+
var repos, pageRepos []string
21+
var nextPage *common.PaginationOption
22+
var err error
23+
24+
firstPage := common.MakePagination(registry.GetMaxPageSize())
25+
catalogOpts := common.CatalogOption{}
26+
27+
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())) {
28+
if err != nil {
29+
return nil, err
30+
}
31+
if len(pageRepos) == 0 {
32+
break
33+
}
34+
repos = append(repos, pageRepos...)
35+
36+
if nextPage == nil || nextPage.Cursor == "" {
37+
break
38+
}
39+
}
40+
return repos, nil
41+
}
42+
43+
func getImageLatestTag(repo string, registry interfaces.IRegistry) (string, error) {
44+
firstPage := common.MakePagination(1000)
45+
var tags []string
46+
withAuth := remote.WithAuth(authn.FromConfig(*registry.GetAuth()))
47+
if latestTags, err := registry.GetLatestTags(repo, 1, withAuth); err == nil {
48+
for _, tag := range latestTags {
49+
if strings.HasSuffix(tag, ".sig") {
50+
continue
51+
}
52+
tagsForDigest := strings.Split(tag, ",")
53+
return tagsForDigest[0], nil
54+
}
55+
} else {
56+
for tagsPage, nextPage, err := registry.List(repo, firstPage, withAuth); ; tagsPage, nextPage, err = registry.List(repo, *nextPage, withAuth) {
57+
if err != nil {
58+
return "", err
59+
}
60+
61+
if slices.Contains(tagsPage, latestTag) {
62+
return latestTag, nil
63+
}
64+
65+
tags = append(tags, tagsPage...)
66+
67+
if nextPage == nil {
68+
break
69+
}
70+
}
71+
return getLatestTag(tags), nil
72+
}
73+
return "", nil
74+
}
75+
76+
func getLatestTag(tags []string) string {
77+
var versions []*semver.Version
78+
for _, tag := range tags {
79+
version, err := semver.NewVersion(tag)
80+
if err == nil {
81+
versions = append(versions, version)
82+
}
83+
}
84+
sort.Sort(sort.Reverse(semver.Collection(versions)))
85+
return versions[0].String()
86+
}

registryclients/harbor.go

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package registryclients
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"github.com/armosec/armoapi-go/armotypes"
7+
"github.com/armosec/registryx/common"
8+
"github.com/armosec/registryx/registries/harbor"
9+
dockerregistry "github.com/docker/docker/api/types/registry"
10+
"github.com/google/go-containerregistry/pkg/authn"
11+
"github.com/google/go-containerregistry/pkg/name"
12+
)
13+
14+
type HarborRegistryClient struct {
15+
Registry *armotypes.HarborImageRegistry
16+
}
17+
18+
func (h *HarborRegistryClient) GetAllRepositories(ctx context.Context) ([]string, error) {
19+
registry, err := name.NewRegistry(h.Registry.InstanceURL)
20+
if err != nil {
21+
return nil, err
22+
}
23+
iRegistry, err := harbor.NewHarborRegistry(&authn.AuthConfig{Username: h.Registry.Username, Password: h.Registry.Password}, &registry, &common.RegistryOptions{})
24+
if err != nil {
25+
return nil, err
26+
}
27+
28+
return getAllRepositories(ctx, iRegistry)
29+
}
30+
31+
func (h *HarborRegistryClient) GetImagesToScan(_ context.Context) (map[string]string, error) {
32+
registry, err := name.NewRegistry(h.Registry.InstanceURL)
33+
if err != nil {
34+
return nil, err
35+
}
36+
iRegistry, err := harbor.NewHarborRegistry(&authn.AuthConfig{Username: h.Registry.Username, Password: h.Registry.Password}, &registry, &common.RegistryOptions{})
37+
if err != nil {
38+
return nil, err
39+
}
40+
41+
images := make(map[string]string, len(h.Registry.Repositories))
42+
for _, repository := range h.Registry.Repositories {
43+
tag, err := getImageLatestTag(repository, iRegistry)
44+
if err != nil {
45+
return nil, err
46+
} else if tag == "" {
47+
return nil, fmt.Errorf("failed to find latest tag for repository %s", repository)
48+
}
49+
images[fmt.Sprintf("%s/%s", h.Registry.InstanceURL, repository)] = tag
50+
}
51+
return images, nil
52+
}
53+
54+
func (h *HarborRegistryClient) GetDockerAuth() *dockerregistry.AuthConfig {
55+
return &dockerregistry.AuthConfig{
56+
Username: h.Registry.Username,
57+
Password: h.Registry.Password,
58+
}
59+
}

registryclients/quay.go

+20-79
Original file line numberDiff line numberDiff line change
@@ -3,55 +3,46 @@ package registryclients
33
import (
44
"context"
55
"fmt"
6-
"github.com/Masterminds/semver/v3"
76
"github.com/armosec/armoapi-go/armotypes"
8-
"github.com/armosec/registryx/common"
9-
"github.com/armosec/registryx/registries"
7+
"github.com/armosec/registryx/registries/quay"
108
dockerregistry "github.com/docker/docker/api/types/registry"
119
"github.com/google/go-containerregistry/pkg/authn"
12-
"github.com/google/go-containerregistry/pkg/v1/remote"
13-
"slices"
14-
"sort"
15-
"strings"
10+
"github.com/google/go-containerregistry/pkg/name"
1611
)
1712

1813
type QuayRegistryClient struct {
1914
Registry *armotypes.QuayImageRegistry
2015
}
2116

2217
func (q *QuayRegistryClient) GetAllRepositories(ctx context.Context) ([]string, error) {
23-
iRegistry, err := registries.Factory(&authn.AuthConfig{Username: q.Registry.RobotAccountName, Password: q.Registry.RobotAccountToken}, q.Registry.ContainerRegistryName, nil)
18+
registry, err := name.NewRegistry(q.Registry.ContainerRegistryName)
2419
if err != nil {
2520
return nil, err
2621
}
27-
var repos, pageRepos []string
28-
var nextPage *common.PaginationOption
29-
30-
firstPage := common.MakePagination(iRegistry.GetMaxPageSize())
31-
catalogOpts := common.CatalogOption{}
32-
33-
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())) {
34-
if err != nil {
35-
return nil, err
36-
}
37-
if len(pageRepos) == 0 {
38-
break
39-
}
40-
repos = append(repos, pageRepos...)
41-
42-
if nextPage == nil || nextPage.Cursor == "" {
43-
break
44-
}
22+
iRegistry, err := quay.NewQuayIORegistry(&authn.AuthConfig{Username: q.Registry.RobotAccountName, Password: q.Registry.RobotAccountToken}, &registry, nil)
23+
if err != nil {
24+
return nil, err
4525
}
46-
return repos, nil
26+
return getAllRepositories(ctx, iRegistry)
4727
}
4828

49-
func (q *QuayRegistryClient) GetImagesToScan(ctx context.Context) (map[string]string, error) {
29+
func (q *QuayRegistryClient) GetImagesToScan(_ context.Context) (map[string]string, error) {
30+
registry, err := name.NewRegistry(q.Registry.ContainerRegistryName)
31+
if err != nil {
32+
return nil, err
33+
}
34+
iRegistry, err := quay.NewQuayIORegistry(&authn.AuthConfig{Username: q.Registry.RobotAccountName, Password: q.Registry.RobotAccountToken}, &registry, nil)
35+
if err != nil {
36+
return nil, err
37+
}
38+
5039
images := make(map[string]string, len(q.Registry.Repositories))
5140
for _, repository := range q.Registry.Repositories {
52-
tag, err := q.getImageLatestTag(ctx, repository)
41+
tag, err := getImageLatestTag(repository, iRegistry)
5342
if err != nil {
5443
return nil, err
44+
} else if tag == "" {
45+
return nil, fmt.Errorf("failed to find latest tag for repository %s", repository)
5546
}
5647
images[fmt.Sprintf("%s/%s", q.Registry.ContainerRegistryName, repository)] = tag
5748
}
@@ -64,53 +55,3 @@ func (q *QuayRegistryClient) GetDockerAuth() *dockerregistry.AuthConfig {
6455
Password: q.Registry.RobotAccountToken,
6556
}
6657
}
67-
68-
func (q *QuayRegistryClient) getImageLatestTag(_ context.Context, repo string) (string, error) {
69-
iRegistry, err := registries.Factory(&authn.AuthConfig{Username: q.Registry.RobotAccountName, Password: q.Registry.RobotAccountToken}, q.Registry.ContainerRegistryName, nil)
70-
if err != nil {
71-
return "", err
72-
}
73-
74-
firstPage := common.MakePagination(1000)
75-
var tags []string
76-
options := []remote.Option{remote.WithAuth(authn.FromConfig(*iRegistry.GetAuth()))}
77-
if latestTags, err := iRegistry.GetLatestTags(repo, 1, options...); err == nil {
78-
for _, tag := range latestTags {
79-
if strings.HasSuffix(tag, ".sig") {
80-
continue
81-
}
82-
tagsForDigest := strings.Split(tag, ",")
83-
return tagsForDigest[0], nil
84-
}
85-
} else {
86-
for tagsPage, nextPage, err := iRegistry.List(repo, firstPage, options...); ; tagsPage, nextPage, err = iRegistry.List(repo, *nextPage) {
87-
if err != nil {
88-
return "", err
89-
}
90-
91-
if slices.Contains(tagsPage, "latest") {
92-
return "latest", nil
93-
}
94-
95-
tags = append(tags, tagsPage...)
96-
97-
if nextPage == nil {
98-
break
99-
}
100-
}
101-
return getLatestTag(tags), nil
102-
}
103-
return "", nil
104-
}
105-
106-
func getLatestTag(tags []string) string {
107-
var versions []*semver.Version
108-
for _, tag := range tags {
109-
version, err := semver.NewVersion(tag)
110-
if err == nil {
111-
versions = append(versions, version)
112-
}
113-
}
114-
sort.Sort(sort.Reverse(semver.Collection(versions)))
115-
return versions[0].String()
116-
}

0 commit comments

Comments
 (0)