Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions image-mapper/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ $ ./image-mapper ghcr.io/stakater/reloader:v1.4.1 registry.k8s.io/sig-storage/li
```

```
$ ./image-mapper ghcr.io/stakater/reloader:v1.4.1 bitnami/postgresql -o csv
$ ./image-mapper ghcr.io/stakater/reloader:v1.4.1 registry.k8s.io/sig-storage/livenessprobe:v2.13.1 -o csv
ghcr.io/stakater/reloader:v1.4.1,[stakater-reloader stakater-reloader-fips]
registry.k8s.io/sig-storage/livenessprobe:v2.13.1,[kubernetes-csi-livenessprobe]
```
Expand All @@ -59,11 +59,25 @@ the `--ignore-tiers` flag.

```
$ ./image-mapper prom/prometheus
prom/prometheus -> prometheus-fips
prom/prometheus -> prometheus
prom/prometheus -> prometheus-fips
prom/prometheus -> prometheus-iamguarded
prom/prometheus -> prometheus-iamguarded-fips

$ ./image-mapper prom/prometheus --ignore-tiers=FIPS
prom/prometheus -> prometheus
prom/prometheus -> prometheus-iamguarded
```

The mapper will also return matches for our `-iamguarded` images. These images
are designed specifically to work with Chainguard's Helm charts. If you aren't
interested in using our charts, you can exclude those matches with
`--ignore-iamguarded`.

```
$ ./image-mapper prom/prometheus --ignore-iamguarded
prom/prometheus -> prometheus
prom/prometheus -> prometheus-fips
```

## Docker
Expand Down
15 changes: 10 additions & 5 deletions image-mapper/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import (
)

var (
outputFormat string
ignoreTiers []string
outputFormat string
ignoreTiers []string
ignoreIamguarded bool
)

var rootCmd = &cobra.Command{
Expand All @@ -26,11 +27,14 @@ var rootCmd = &cobra.Command{
return fmt.Errorf("constructing output: %w", err)
}

var opts []mapper.Option
var ignoreFns []mapper.IgnoreFn
if len(ignoreTiers) > 0 {
opts = append(opts, mapper.WithoutTiers(ignoreTiers))
ignoreFns = append(ignoreFns, mapper.IgnoreTiers(ignoreTiers))
}
m, err := mapper.NewMapper(ctx, opts...)
if ignoreIamguarded {
ignoreFns = append(ignoreFns, mapper.IgnoreIamguarded())
}
m, err := mapper.NewMapper(ctx, mapper.WithIgnoreFns(ignoreFns...))
if err != nil {
return fmt.Errorf("creating mapper: %w", err)
}
Expand All @@ -52,6 +56,7 @@ var rootCmd = &cobra.Command{
func init() {
rootCmd.Flags().StringVarP(&outputFormat, "output", "o", "text", "Output format (csv, json, text, customer-yaml)")
rootCmd.Flags().StringSliceVar(&ignoreTiers, "ignore-tiers", []string{}, "Ignore Chainguard repos of specific tiers (PREMIUM, APPLICATION, BASE, FIPS, AI)")
rootCmd.Flags().BoolVar(&ignoreIamguarded, "ignore-iamguarded", false, "Ignore iamguarded images")
}

func Execute() error {
Expand Down
27 changes: 27 additions & 0 deletions image-mapper/internal/mapper/ignore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package mapper

import (
"slices"
"strings"
)

// IgnoreFn configures a mapper to ignore repositories
type IgnoreFn func(Repo) bool

// IgnoreTiers ignores repos that are in the provided tiers
func IgnoreTiers(tiers []string) IgnoreFn {
var ignoreTiers []string
for _, tier := range tiers {
ignoreTiers = append(ignoreTiers, strings.ToLower(tier))
}
return func(repo Repo) bool {
return slices.Contains(ignoreTiers, strings.ToLower(repo.CatalogTier))
}
}

// IgnoreIamguarded ignores iamguarded repos
func IgnoreIamguarded() IgnoreFn {
return func(repo Repo) bool {
return strings.HasSuffix(repo.Name, "iamguarded") || strings.HasSuffix(repo.Name, "iamguarded-fips")
}
}
229 changes: 229 additions & 0 deletions image-mapper/internal/mapper/ignore_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
package mapper

import (
"testing"
)

func TestIgnoreTiers(t *testing.T) {
tests := []struct {
name string
tiers []string
repo Repo
wantIgnore bool
}{
{
name: "exact match - FIPS",
tiers: []string{"FIPS"},
repo: Repo{
Name: "test-repo",
CatalogTier: "FIPS",
},
wantIgnore: true,
},
{
name: "case insensitive match - lowercase tier input",
tiers: []string{"fips"},
repo: Repo{
Name: "test-repo",
CatalogTier: "FIPS",
},
wantIgnore: true,
},
{
name: "case insensitive match - lowercase repo tier",
tiers: []string{"FIPS"},
repo: Repo{
Name: "test-repo",
CatalogTier: "fips",
},
wantIgnore: true,
},
{
name: "case insensitive match - mixed case",
tiers: []string{"FiPs"},
repo: Repo{
Name: "test-repo",
CatalogTier: "fIpS",
},
wantIgnore: true,
},
{
name: "multiple tiers - first matches",
tiers: []string{"FIPS", "BASE", "APPLICATION"},
repo: Repo{
Name: "test-repo",
CatalogTier: "FIPS",
},
wantIgnore: true,
},
{
name: "multiple tiers - middle matches",
tiers: []string{"FIPS", "BASE", "APPLICATION"},
repo: Repo{
Name: "test-repo",
CatalogTier: "BASE",
},
wantIgnore: true,
},
{
name: "multiple tiers - last matches",
tiers: []string{"FIPS", "BASE", "APPLICATION"},
repo: Repo{
Name: "test-repo",
CatalogTier: "APPLICATION",
},
wantIgnore: true,
},
{
name: "no match",
tiers: []string{"FIPS"},
repo: Repo{
Name: "test-repo",
CatalogTier: "BASE",
},
wantIgnore: false,
},
{
name: "multiple tiers - no match",
tiers: []string{"FIPS", "BASE", "APPLICATION"},
repo: Repo{
Name: "test-repo",
CatalogTier: "AI",
},
wantIgnore: false,
},
{
name: "empty tier list",
tiers: []string{},
repo: Repo{
Name: "test-repo",
CatalogTier: "FIPS",
},
wantIgnore: false,
},
{
name: "empty catalog tier",
tiers: []string{"FIPS"},
repo: Repo{
Name: "test-repo",
CatalogTier: "",
},
wantIgnore: false,
},
{
name: "empty string in tiers list matches empty catalog tier",
tiers: []string{""},
repo: Repo{
Name: "test-repo",
CatalogTier: "",
},
wantIgnore: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ignoreFn := IgnoreTiers(tt.tiers)
got := ignoreFn(tt.repo)
if got != tt.wantIgnore {
t.Errorf("IgnoreTiers() = %v, want %v", got, tt.wantIgnore)
}
})
}
}

func TestIgnoreIamguarded(t *testing.T) {
tests := []struct {
name string
repo Repo
wantIgnore bool
}{
{
name: "repo ending with iamguarded",
repo: Repo{
Name: "test-repo-iamguarded",
},
wantIgnore: true,
},
{
name: "repo ending with iamguarded-fips",
repo: Repo{
Name: "test-repo-iamguarded-fips",
},
wantIgnore: true,
},
{
name: "repo with just iamguarded",
repo: Repo{
Name: "iamguarded",
},
wantIgnore: true,
},
{
name: "repo with just iamguarded-fips",
repo: Repo{
Name: "iamguarded-fips",
},
wantIgnore: true,
},
{
name: "repo not ending with iamguarded",
repo: Repo{
Name: "test-repo",
},
wantIgnore: false,
},
{
name: "repo containing iamguarded but not at end",
repo: Repo{
Name: "iamguarded-test-repo",
},
wantIgnore: false,
},
{
name: "repo containing iamguarded-fips but not at end",
repo: Repo{
Name: "iamguarded-fips-test",
},
wantIgnore: false,
},
{
name: "empty repo name",
repo: Repo{
Name: "",
},
wantIgnore: false,
},
{
name: "case sensitive - uppercase IAMGUARDED",
repo: Repo{
Name: "test-repo-IAMGUARDED",
},
wantIgnore: false,
},
{
name: "case sensitive - uppercase IAMGUARDED-FIPS",
repo: Repo{
Name: "test-repo-IAMGUARDED-FIPS",
},
wantIgnore: false,
},
{
name: "partial match - iamguarde (missing d)",
repo: Repo{
Name: "test-repo-iamguarde",
},
wantIgnore: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ignoreFn := IgnoreIamguarded()
got := ignoreFn(tt.repo)
if got != tt.wantIgnore {
t.Errorf("IgnoreIamguarded() = %v, want %v", got, tt.wantIgnore)
}
})
}
}
24 changes: 16 additions & 8 deletions image-mapper/internal/mapper/mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"fmt"
"slices"
"strings"

"github.com/google/go-containerregistry/pkg/name"
)
Expand All @@ -17,8 +16,8 @@ type Mapping struct {

// Mapper maps image references to images in our catalog
type Mapper struct {
repos []Repo
ignoreTiers []string
repos []Repo
ignoreFns []IgnoreFn
}

// NewMapper creates a new mapper
Expand All @@ -34,8 +33,8 @@ func NewMapper(ctx context.Context, opts ...Option) (*Mapper, error) {
}

m := &Mapper{
repos: repos,
ignoreTiers: o.ignoreTiers,
repos: repos,
ignoreFns: o.ignoreFns,
}

return m, nil
Expand Down Expand Up @@ -86,9 +85,7 @@ func (m *Mapper) Map(image string) (*Mapping, error) {
continue
}

// Exclude specific tiers. Useful for ignoring 'FIPS' tier
// images when they aren't relevant.
if slices.Contains(m.ignoreTiers, strings.ToLower(cgrrepo.CatalogTier)) {
if m.ignoreRepo(cgrrepo) {
continue
}

Expand All @@ -109,3 +106,14 @@ func (m *Mapper) Map(image string) (*Mapping, error) {
Results: results,
}, nil
}

func (m *Mapper) ignoreRepo(repo Repo) bool {
for _, ignore := range m.ignoreFns {
if !ignore(repo) {
continue
}
return true
}

return false
}
Loading
Loading