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
128 changes: 58 additions & 70 deletions image-mapper/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,108 +2,96 @@

A tool for matching non-Chainguard images to their Chainguard equivalents.

## Usage
## Build

Build the tool.

```
$ go build -o image-mapper .
```

Then, provide the images to map on the command line.
You can also build and run the tool with Docker.

```
$ ./image-mapper ghcr.io/stakater/reloader:v1.4.1 registry.k8s.io/sig-storage/livenessprobe:v2.13.1
ghcr.io/stakater/reloader:v1.4.1 -> cgr.dev/chainguard/stakater-reloader-fips:v1.4.12
ghcr.io/stakater/reloader:v1.4.1 -> cgr.dev/chainguard/stakater-reloader:v1.4.12
registry.k8s.io/sig-storage/livenessprobe:v2.13.1 -> cgr.dev/chainguard/kubernetes-csi-livenessprobe:v2.17.0
```

You'll notice that the mapper increments the tag to the closest version
supported by Chainguard. To benefit from continued CVE remediation, it's
important, where possible, to use tags that are being actively maintained.
# Build the image
docker build -t image-mapper .

You can also provide a list of images (one image per line) via stdin when the first
argument is `-`.
# Run for an individual image
docker run -it --rm image-mapper map ghcr.io/stakater/reloader:v1.4.1

```
$ cat ./images.txt | ./image-mapper -
# Or, pass a list of images from a text file
docker run -i --rm image-mapper -- map - < images.txt
```

## Options
## Basic Usage

### Output
### Map

Configure the output format with the `-o` flag. Supported formats are: `csv`,
`json` and `text`.
The `map` command maps images provided on the command line.

```
$ ./image-mapper ghcr.io/stakater/reloader:v1.4.1 registry.k8s.io/sig-storage/livenessprobe:v2.13.1 -o json | jq -r .
[
{
"image": "ghcr.io/stakater/reloader:v1.4.1",
"results": [
"cgr.dev/chainguard/stakater-reloader-fips:v1.4.12",
"cgr.dev/chainguard/stakater-reloader:v1.4.12"
]
},
{
"image": "registry.k8s.io/sig-storage/livenessprobe:v2.13.1",
"results": [
"cgr.dev/chainguard/kubernetes-csi-livenessprobe:v2.17.0"
]
}
]
$ ./image-mapper map ghcr.io/stakater/reloader:v1.4.1 registry.k8s.io/sig-storage/livenessprobe:v2.13.1
ghcr.io/stakater/reloader:v1.4.1 -> cgr.dev/chainguard/stakater-reloader-fips:v1.4.12
ghcr.io/stakater/reloader:v1.4.1 -> cgr.dev/chainguard/stakater-reloader:v1.4.12
registry.k8s.io/sig-storage/livenessprobe:v2.13.1 -> cgr.dev/chainguard/kubernetes-csi-livenessprobe:v2.17.0
```

You can also provide a list of images (one image per line) via stdin when the first
argument is `-`.

```
$ ./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,[cgr.dev/chainguard/stakater-reloader-fips:v1.4.12 cgr.dev/chainguard/stakater-reloader:v1.4.12]
registry.k8s.io/sig-storage/livenessprobe:v2.13.1,[cgr.dev/chainguard/kubernetes-csi-livenessprobe:v2.17.0]
$ cat ./images.txt | ./image-mapper map -
ghcr.io/stakater/reloader:v1.4.1 -> cgr.dev/chainguard/stakater-reloader-fips:v1.4.12
ghcr.io/stakater/reloader:v1.4.1 -> cgr.dev/chainguard/stakater-reloader:v1.4.12
registry.k8s.io/sig-storage/livenessprobe:v2.13.1 -> cgr.dev/chainguard/kubernetes-csi-livenessprobe:v2.17.0
```

### Ignore Tiers (i.e FIPS)

The output will map both FIPS and non-FIPS variants. You can exclude FIPS with
the `--ignore-tiers` flag.
You'll notice that the mapper increments the tag to the closest version
supported by Chainguard. To benefit from continued CVE remediation, it's
important, where possible, to use tags that are being actively maintained.

```
$ ./image-mapper prom/prometheus
prom/prometheus -> cgr.dev/chainguard/prometheus-fips:latest
prom/prometheus -> cgr.dev/chainguard/prometheus-iamguarded-fips:latest
prom/prometheus -> cgr.dev/chainguard/prometheus-iamguarded:latest
prom/prometheus -> cgr.dev/chainguard/prometheus:latest

$ ./image-mapper prom/prometheus --ignore-tiers=FIPS
prom/prometheus -> cgr.dev/chainguard/prometheus-iamguarded:latest
prom/prometheus -> cgr.dev/chainguard/prometheus:latest
```
Refer to [this page](./docs/map.md) for more details.

### Ignore Iamguarded
### Helm

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`.
The `helm-chart` and `helm-values` subcommands extract image related values and
map them to Chainguard.

```
$ ./image-mapper prom/prometheus --ignore-iamguarded
prom/prometheus -> cgr.dev/chainguard/prometheus-fips:latest
prom/prometheus -> cgr.dev/chainguard/prometheus:latest
$ ./image-mapper map helm-chart argocd/argo-cd
redis-ha:
image:
repository: cgr.dev/chainguard/redis # Original: ecr-public.aws.com/docker/library/redis
tag: 8.2.2 # Original: 8.2.2-alpine
configmapTest:
image:
repository: cgr.dev/chainguard/shellcheck # Original: koalaman/shellcheck
tag: v0.11.0-dev # Original: v0.10.0
haproxy:
image:
repository: cgr.dev/chainguard/haproxy # Original: ecr-public.aws.com/docker/library/haproxy
exporter:
image: cgr.dev/chainguard/prometheus-redis-exporter # Original: ghcr.io/oliver006/redis_exporter
global:
image:
repository: cgr.dev/chainguard/argocd # Original: quay.io/argoproj/argocd
...
```

## Docker

```
# Build the image
docker build -t image-mapper .
$ helm show values argocd/argo-cd | ./image-mapper map helm-values -
global:
image:
repository: cgr.dev/chainguard/argocd # Original: quay.io/argoproj/argocd
dex:
image:
repository: cgr.dev/chainguard/dex # Original: ghcr.io/dexidp/dex
...
```

# Run for an individual image
docker run -it --rm image-mapper ghcr.io/stakater/reloader:v1.4.1
These commands provide values overrides that you can pass to `helm install`.

# Or, pass a list of images from a text file
docker run -i --rm image-mapper -- - < images.txt
```
Refer to [this page](./docs/map_helm.md) for more details.

## Development

Expand Down
71 changes: 71 additions & 0 deletions image-mapper/cmd/map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package cmd

import (
"fmt"
"os"

"github.com/chainguard-dev/customer-success/scripts/image-mapper/internal/mapper"
"github.com/spf13/cobra"
)

func init() {
rootCmd.AddCommand(
MapCommand(),
)
}

func MapCommand() *cobra.Command {
opts := struct {
OutputFormat string
IgnoreTiers []string
IgnoreIamguarded bool
Repo string
}{}
cmd := &cobra.Command{
Use: "map",
Short: "Map upstream image references to Chainguard images.",
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
output, err := mapper.NewOutput(opts.OutputFormat)
if err != nil {
return fmt.Errorf("constructing output: %w", err)
}

var ignoreFns []mapper.IgnoreFn
if len(opts.IgnoreTiers) > 0 {
ignoreFns = append(ignoreFns, mapper.IgnoreTiers(opts.IgnoreTiers))
}
if opts.IgnoreIamguarded {
ignoreFns = append(ignoreFns, mapper.IgnoreIamguarded())
}
m, err := mapper.NewMapper(cmd.Context(), mapper.WithRepository(opts.Repo), mapper.WithIgnoreFns(ignoreFns...))
if err != nil {
return fmt.Errorf("creating mapper: %w", err)
}

it := mapper.NewArgsIterator(args)
if args[0] == "-" {
it = mapper.NewReaderIterator(os.Stdin)
}

mappings, err := m.MapAll(it)
if err != nil {
return fmt.Errorf("mapping images: %w", err)
}

return output(os.Stdout, mappings)
},
}

rootCmd.Flags().StringVarP(&opts.OutputFormat, "output", "o", "text", "Output format (csv, json, text, customer-yaml)")
rootCmd.Flags().StringSliceVar(&opts.IgnoreTiers, "ignore-tiers", []string{}, "Ignore Chainguard repos of specific tiers (PREMIUM, APPLICATION, BASE, FIPS, AI)")
rootCmd.Flags().BoolVar(&opts.IgnoreIamguarded, "ignore-iamguarded", false, "Ignore iamguarded images")
rootCmd.Flags().StringVar(&opts.Repo, "repository", "cgr.dev/chainguard", "Modifies the repository URI in the mappings. For instance, registry.internal.dev/chainguard would result in registry.internal.dev/chainguard/<image> in the output.")

cmd.AddCommand(
MapHelmChartCommand(),
MapHelmValuesCommand(),
)

return cmd
}
118 changes: 118 additions & 0 deletions image-mapper/cmd/map_helm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package cmd

import (
"fmt"
"io"
"os"

"github.com/chainguard-dev/customer-success/scripts/image-mapper/internal/helm"
"github.com/chainguard-dev/customer-success/scripts/image-mapper/internal/mapper"
"github.com/spf13/cobra"
)

func MapHelmChartCommand() *cobra.Command {
opts := struct {
Repo string
ChartRepo string
ChartVersion string
}{}
cmd := &cobra.Command{
Use: "helm-chart",
Short: "Extract image related values from a Helm chart and map them to Chainguard.",
Example: `
# Map a Helm chart. This requires that the Chart repo has been added with 'helm repo add' beforehand.
image-mapper map helm-chart argocd/argo-cd

# Override the repository in the mappings with your own mirror or proxy. For instance, cgr.dev/chainguard/<image> would become registry.internal/cgr/<image> in the output.
image-mapper map helm-chart argocd/argo-cd --repository=registry.internal/cgr

# Map a specific version of a Helm chart.
image-mapper map helm-chart argocd/argo-cd --chart-version=9.0.0

# Specify a remote Chart repostory. This means the repository doesn't need to be added with 'helm repo add'.
image-mapper map helm-chart argo-cd --chart-repo=https://argoproj.github.io/argo-helm

# Specify a specific version of a remote Chart.
image-mapper map helm-chart argo-cd --chart-repo=https://argoproj.github.io/argo-helm --chart-version=9.0.0
`,
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
chart := helm.ChartDescriptor{
Name: args[0],
Repository: opts.ChartRepo,
Version: opts.ChartVersion,
}
output, err := helm.MapChart(cmd.Context(), chart, mapper.WithRepository(opts.Repo))
if err != nil {
return fmt.Errorf("mapping values: %w", err)
}

if _, err := os.Stdout.Write(output); err != nil {
return fmt.Errorf("writing output: %w", err)
}

return nil
},
}

cmd.Flags().StringVar(&opts.Repo, "repository", "cgr.dev/chainguard", "Modifies the repository URI in the mappings. For instance, registry.internal.dev/chainguard would result in registry.internal.dev/chainguard/<image> in the output.")
cmd.Flags().StringVar(&opts.ChartRepo, "chart-repo", "", "The chart repository url to locate the requested chart.")
cmd.Flags().StringVar(&opts.ChartVersion, "chart-version", "", "A version constraint for the chart version.")

return cmd
}

func MapHelmValuesCommand() *cobra.Command {
opts := struct {
Repo string
}{}
cmd := &cobra.Command{
Use: "helm-values",
Short: "Extract image related values from a Helm values file and map them to Chainguard.",
Example: `
# Map images in the values returned by 'helm show values'
helm show values argocd/argo-cd | image-mapper map helm-values -

# Map images in a values file on disk.
helm show values argocd/argo-cd > values.yaml
image-mapper map helm-values values.yaml

# Override the repository in the mappings with your own mirror or proxy. For instance, cgr.dev/chainguard/<image> would become registry.internal/cgr/<image> in the output.
image-mapper map helm-values values.yaml --repository=registry.internal/cgr
`,
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
var (
input []byte
err error
)
switch args[0] {
case "-":
input, err = io.ReadAll(os.Stdin)
if err != nil {
return fmt.Errorf("reading stdin: %w", err)
}
default:
input, err = os.ReadFile(args[0])
if err != nil {
return fmt.Errorf("reading file: %s: %w", args[0], err)
}
}

output, err := helm.MapValues(cmd.Context(), input, mapper.WithRepository(opts.Repo))
if err != nil {
return fmt.Errorf("mapping values: %w", err)
}

if _, err := os.Stdout.Write(output); err != nil {
return fmt.Errorf("writing output: %w", err)
}

return nil
},
}

cmd.Flags().StringVar(&opts.Repo, "repository", "cgr.dev/chainguard", "Modifies the repository URI in the mappings. For instance, registry.internal.dev/chainguard would result in registry.internal.dev/chainguard/<image> in the output.")

return cmd
}
Loading
Loading