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
17 changes: 17 additions & 0 deletions docs/helm_annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ By default, Artifact Hub will try to extract the containers images used by Helm

Containers images will be scanned for security vulnerabilities. The security report generated will be available in the package detail view. It is possible to whitelist images so that they are not scanned by setting the `whitelisted` flag to true.

When the standard [helm.sh/images](#standard-annotations) annotation is also present, the images listed in it are processed first and the ones provided in this annotation are added to that list. Entries using an image reference already listed in the `helm.sh/images` annotation override the existing one, which allows extending them with Artifact Hub specific fields like `whitelisted` or `platforms`.

- **artifacthub.io/crds** *(yaml string, see example below)*

This annotation can be used to list the operator's CRDs. They will be visible in the package's detail view as cards.
Expand Down Expand Up @@ -82,12 +84,27 @@ This annotation can be used to provide some screenshots that will be featured in

This annotation can be used to provide some information about the key used to sign a given chart version. This information will be displayed on the Artifact Hub UI, making it easier for users to get the information they need to verify the integrity and origin of your chart. The `url` field indicates where users can find the public key and it is mandatory when a sign key entry is provided.

## Standard annotations

In addition to the Artifact Hub specific annotations listed above, some standard annotations defined by the Helm community are supported as well.

- **helm.sh/images** *(yaml string, see example below)*

This annotation, defined in [HIP-0015](https://github.com/helm/community/blob/main/hips/hip-0015.md), allows chart maintainers to list the containers images used by the chart. Each entry must provide at least a `name` and an `image` field. Additional fields, like the Artifact Hub specific `whitelisted` and `platforms` ones, may be provided as well.

The images listed in this annotation are processed **before** the ones in the `artifacthub.io/images` annotation, and both lists are combined. Please see the [artifacthub.io/images](#supported-annotations) annotation for more details about how they are merged. Containers images provided this way replace the ones Artifact Hub extracts automatically from the manifests generated from a dry-run install using the default values.

## Example

Artifact Hub annotations in `Chart.yaml`:

```yaml
annotations:
helm.sh/images: |
- name: img1
image: repo/img1:1.0.0
- name: img2
image: repo/img2:2.0.0
artifacthub.io/category: security
artifacthub.io/changes: |
- Added cool feature
Expand Down
69 changes: 65 additions & 4 deletions internal/tracker/source/helm/helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ const (
securityUpdatesAnnotation = "artifacthub.io/containsSecurityUpdates"
signKeyAnnotation = "artifacthub.io/signKey"

helmImagesAnnotation = "helm.sh/images"

legacyChartContentLayerMediaType = "application/tar+gzip"
ChartContentLayerMediaType = "application/vnd.cncf.helm.chart.content.v1.tar+gzip"
ChartProvenanceLayerMediaType = "application/vnd.cncf.helm.chart.provenance.v1.prov"
Expand Down Expand Up @@ -659,11 +661,34 @@ func EnrichPackageFromAnnotations(p *hub.Package, annotations map[string]string)
}

// Images
if v, ok := annotations[imagesAnnotation]; ok {
// The images provided in the helm.sh/images annotation are processed
// first. The ones provided in the artifacthub.io/images annotation are
// added to that list, overriding the entries that use the same image
// reference.
helmImagesValue, helmImagesProvided := annotations[helmImagesAnnotation]
hubImagesValue, hubImagesProvided := annotations[imagesAnnotation]
if helmImagesProvided || hubImagesProvided {
var images []*hub.ContainerImage
if err := yaml.Unmarshal([]byte(v), &images); err != nil {
errs = multierror.Append(errs, fmt.Errorf("%w: invalid images value", errInvalidAnnotation))
} else {
imagesParsed := true
if helmImagesProvided {
helmImages, err := parseContainersImages(helmImagesValue)
if err != nil {
errs = multierror.Append(errs, fmt.Errorf("%w: invalid helm images value", errInvalidAnnotation))
imagesParsed = false
} else {
images = helmImages
}
}
if hubImagesProvided {
hubImages, err := parseContainersImages(hubImagesValue)
if err != nil {
errs = multierror.Append(errs, fmt.Errorf("%w: invalid images value", errInvalidAnnotation))
imagesParsed = false
} else {
images = mergeContainersImages(images, hubImages)
}
}
if imagesParsed {
if err := pkg.ValidateContainersImages(hub.Helm, images); err != nil {
errs = multierror.Append(errs, fmt.Errorf("%w: %w", errInvalidAnnotation, err))
} else {
Expand Down Expand Up @@ -821,3 +846,39 @@ func contains(l []string, e string) bool {
}
return false
}

// parseContainersImages parses the containers images provided in an
// annotation's value.
func parseContainersImages(v string) ([]*hub.ContainerImage, error) {
var images []*hub.ContainerImage
if err := yaml.Unmarshal([]byte(v), &images); err != nil {
return nil, err
}
for _, image := range images {
if image == nil {
return nil, errors.New("invalid containers images entry")
}
}
return images, nil
}

// mergeContainersImages adds the extra containers images provided to the base
// list. Entries using an image reference already present in the base list
// override the existing one.
func mergeContainersImages(base, extra []*hub.ContainerImage) []*hub.ContainerImage {
images := base
for _, extraImage := range extra {
var found bool
for i, image := range images {
if image.Image == extraImage.Image {
images[i] = extraImage
found = true
break
}
}
if !found {
images = append(images, extraImage)
}
}
return images
}
82 changes: 82 additions & 0 deletions internal/tracker/source/helm/helm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,88 @@ func TestEnrichPackageFromAnnotations(t *testing.T) {
&hub.Package{},
"invalid image reference: could not parse reference",
},
{
&hub.Package{},
map[string]string{
helmImagesAnnotation: `
- name: img1
image: repo/img1:1.0.0
- name: img2
image: repo/img2:2.0.0
`,
},
&hub.Package{
ContainersImages: []*hub.ContainerImage{
{
Name: "img1",
Image: "repo/img1:1.0.0",
},
{
Name: "img2",
Image: "repo/img2:2.0.0",
},
},
},
"",
},
{
&hub.Package{},
map[string]string{
helmImagesAnnotation: `
- name: img1
image: repo/img1:1.0.0
- name: img2
image: repo/img2:2.0.0
`,
imagesAnnotation: `
- name: img2
image: repo/img2:2.0.0
whitelisted: true
- name: img3
image: repo/img3:3.0.0
platforms:
- linux/amd64
`,
},
&hub.Package{
ContainersImages: []*hub.ContainerImage{
{
Name: "img1",
Image: "repo/img1:1.0.0",
},
{
Name: "img2",
Image: "repo/img2:2.0.0",
Whitelisted: true,
},
{
Name: "img3",
Image: "repo/img3:3.0.0",
Platforms: []string{"linux/amd64"},
},
},
},
"",
},
{
&hub.Package{},
map[string]string{
helmImagesAnnotation: `
- name: img1
image: ":"
`,
},
&hub.Package{},
"invalid image reference: could not parse reference",
},
{
&hub.Package{},
map[string]string{
helmImagesAnnotation: "-",
},
&hub.Package{},
"invalid annotation: invalid helm images value",
},
// License
{
&hub.Package{},
Expand Down
Loading