Skip to content

Commit 8fe7be4

Browse files
authored
image-mapper: map images to tags (#255)
Rather than just mapping to the repository name, let's map to the full image reference, including the closest active tag. This essentially provides the images they should consider migrating to based on our best practice advice. Currently only works for tags that adhere to semver but this could be expanded to support other heuristics for tag matching.
1 parent 1e60570 commit 8fe7be4

File tree

6 files changed

+664
-190
lines changed

6 files changed

+664
-190
lines changed

image-mapper/README.md

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# image-mapper
22

3-
An example of matching non-Chainguard images to their Chainguard equivalents.
3+
A tool for matching non-Chainguard images to their Chainguard equivalents.
44

55
## Usage
66

@@ -14,18 +14,26 @@ Then, provide the images to map on the command line.
1414

1515
```
1616
$ ./image-mapper ghcr.io/stakater/reloader:v1.4.1 registry.k8s.io/sig-storage/livenessprobe:v2.13.1
17-
ghcr.io/stakater/reloader:v1.4.1 -> stakater-reloader
18-
ghcr.io/stakater/reloader:v1.4.1 -> stakater-reloader-fips
19-
registry.k8s.io/sig-storage/livenessprobe:v2.13.1 -> kubernetes-csi-livenessprobe
17+
ghcr.io/stakater/reloader:v1.4.1 -> cgr.dev/chainguard/stakater-reloader-fips:v1.4.12
18+
ghcr.io/stakater/reloader:v1.4.1 -> cgr.dev/chainguard/stakater-reloader:v1.4.12
19+
registry.k8s.io/sig-storage/livenessprobe:v2.13.1 -> cgr.dev/chainguard/kubernetes-csi-livenessprobe:v2.17.0
2020
```
2121

22-
You can provide a list of images (one image per line) via stdin when the first
22+
You'll notice that the mapper increments the tag to the closest version
23+
supported by Chainguard. To benefit from continued CVE remediation, it's
24+
important, where possible, to use tags that are being actively maintained.
25+
26+
You can also provide a list of images (one image per line) via stdin when the first
2327
argument is `-`.
2428

2529
```
2630
$ cat ./images.txt | ./image-mapper -
2731
```
2832

33+
## Options
34+
35+
### Output
36+
2937
Configure the output format with the `-o` flag. Supported formats are: `csv`,
3038
`json` and `text`.
3139

@@ -35,49 +43,53 @@ $ ./image-mapper ghcr.io/stakater/reloader:v1.4.1 registry.k8s.io/sig-storage/li
3543
{
3644
"image": "ghcr.io/stakater/reloader:v1.4.1",
3745
"results": [
38-
"stakater-reloader",
39-
"stakater-reloader-fips"
46+
"cgr.dev/chainguard/stakater-reloader-fips:v1.4.12",
47+
"cgr.dev/chainguard/stakater-reloader:v1.4.12"
4048
]
4149
},
4250
{
4351
"image": "registry.k8s.io/sig-storage/livenessprobe:v2.13.1",
4452
"results": [
45-
"kubernetes-csi-livenessprobe"
53+
"cgr.dev/chainguard/kubernetes-csi-livenessprobe:v2.17.0"
4654
]
4755
}
4856
]
4957
```
5058

5159
```
5260
$ ./image-mapper ghcr.io/stakater/reloader:v1.4.1 registry.k8s.io/sig-storage/livenessprobe:v2.13.1 -o csv
53-
ghcr.io/stakater/reloader:v1.4.1,[stakater-reloader stakater-reloader-fips]
54-
registry.k8s.io/sig-storage/livenessprobe:v2.13.1,[kubernetes-csi-livenessprobe]
61+
ghcr.io/stakater/reloader:v1.4.1,[cgr.dev/chainguard/stakater-reloader-fips:v1.4.12 cgr.dev/chainguard/stakater-reloader:v1.4.12]
62+
registry.k8s.io/sig-storage/livenessprobe:v2.13.1,[cgr.dev/chainguard/kubernetes-csi-livenessprobe:v2.17.0]
5563
```
5664

65+
### Ignore Tiers (i.e FIPS)
66+
5767
The output will map both FIPS and non-FIPS variants. You can exclude FIPS with
5868
the `--ignore-tiers` flag.
5969

6070
```
6171
$ ./image-mapper prom/prometheus
62-
prom/prometheus -> prometheus
63-
prom/prometheus -> prometheus-fips
64-
prom/prometheus -> prometheus-iamguarded
65-
prom/prometheus -> prometheus-iamguarded-fips
72+
prom/prometheus -> cgr.dev/chainguard/prometheus-fips:latest
73+
prom/prometheus -> cgr.dev/chainguard/prometheus-iamguarded-fips:latest
74+
prom/prometheus -> cgr.dev/chainguard/prometheus-iamguarded:latest
75+
prom/prometheus -> cgr.dev/chainguard/prometheus:latest
6676
6777
$ ./image-mapper prom/prometheus --ignore-tiers=FIPS
68-
prom/prometheus -> prometheus
69-
prom/prometheus -> prometheus-iamguarded
78+
prom/prometheus -> cgr.dev/chainguard/prometheus-iamguarded:latest
79+
prom/prometheus -> cgr.dev/chainguard/prometheus:latest
7080
```
7181

82+
### Ignore Iamguarded
83+
7284
The mapper will also return matches for our `-iamguarded` images. These images
7385
are designed specifically to work with Chainguard's Helm charts. If you aren't
7486
interested in using our charts, you can exclude those matches with
7587
`--ignore-iamguarded`.
7688

7789
```
7890
$ ./image-mapper prom/prometheus --ignore-iamguarded
79-
prom/prometheus -> prometheus
80-
prom/prometheus -> prometheus-fips
91+
prom/prometheus -> cgr.dev/chainguard/prometheus-fips:latest
92+
prom/prometheus -> cgr.dev/chainguard/prometheus:latest
8193
```
8294

8395
## Docker

image-mapper/internal/mapper/mapper.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"slices"
7+
"strings"
78

89
"github.com/google/go-containerregistry/pkg/name"
910
)
@@ -18,6 +19,7 @@ type Mapping struct {
1819
type Mapper struct {
1920
repos []Repo
2021
ignoreFns []IgnoreFn
22+
repoName string
2123
}
2224

2325
// NewMapper creates a new mapper
@@ -35,6 +37,7 @@ func NewMapper(ctx context.Context, opts ...Option) (*Mapper, error) {
3537
m := &Mapper{
3638
repos: repos,
3739
ignoreFns: o.ignoreFns,
40+
repoName: "cgr.dev/chainguard",
3841
}
3942

4043
return m, nil
@@ -71,12 +74,14 @@ func (m *Mapper) MapAll(it Iterator) ([]*Mapping, error) {
7174

7275
// Map an upstream image to the corresponding images in chainguard-private
7376
func (m *Mapper) Map(image string) (*Mapping, error) {
74-
ref, err := name.ParseReference(image)
77+
ref, err := name.NewTag(strings.Split(image, "@")[0])
7578
if err != nil {
7679
return nil, fmt.Errorf("parsing %s: %w", image, err)
7780
}
7881

79-
matches := map[string]struct{}{}
82+
// Identify repositories in the Chainguard catalog that match the
83+
// provided image
84+
matches := map[string]Repo{}
8085
for _, cgrrepo := range m.repos {
8186
// There are some images that may appear in the results but are
8287
// not accessible in the catalog. We can exclude them by
@@ -92,12 +97,22 @@ func (m *Mapper) Map(image string) (*Mapping, error) {
9297
if !Match(ref, cgrrepo) {
9398
continue
9499
}
95-
matches[cgrrepo.Name] = struct{}{}
100+
matches[cgrrepo.Name] = cgrrepo
96101
}
97102

103+
// Format the matches into the results we'll include in the mappings
104+
98105
results := []string{}
99-
for match := range matches {
100-
results = append(results, match)
106+
for _, cgrrepo := range matches {
107+
// Append the repository name to the rest of the reference
108+
result := fmt.Sprintf("%s/%s", m.repoName, cgrrepo.Name)
109+
110+
// Try and match the provided tag to one of the active tags
111+
tag := MatchTag(cgrrepo.ActiveTags, ref.TagStr())
112+
if tag != "" {
113+
result = fmt.Sprintf("%s:%s", result, tag)
114+
}
115+
results = append(results, result)
101116
}
102117
slices.Sort(results)
103118

0 commit comments

Comments
 (0)