mirror of
https://github.com/git-pkgs/proxy.git
synced 2026-11-09 07:39:19 -05:00
Mirroring pkg:helm PURLs failed with an unsupported ecosystem error. The
namespace now names the upstream.helm repository, as the Helm handler
caches charts per repository: pkg:helm/{repository}/{chart}@{version}.
The chart is found in the repository's index through the handler's own
index lookup, fetched from the first URL the release lists, as helm does,
checked against the listed digest and cached under the repository,
digest and filename the handler serves it from. The index goes through
the metadata cache, so helm finds the chart there while the repository is
down. Unversioned PURLs list every version in the index.
Charts without a repository, from an unconfigured one, or listed in an
OCI registry are refused; the last are pulled through /v2, and the error
names the pkg:oci PURL that mirrors them.
67 lines
2.7 KiB
Go
67 lines
2.7 KiB
Go
package handler
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"path"
|
|
"slices"
|
|
"strings"
|
|
)
|
|
|
|
// MirrorChart caches a chart release of a configured repository under the
|
|
// key handleChart serves it from, checked against the digest the index
|
|
// lists. The index is read through the metadata cache, as handleIndex reads
|
|
// it, so with metadata caching on helm finds the chart there as well while
|
|
// the repository is down.
|
|
func (h *HelmHandler) MirrorChart(ctx context.Context, repository, chart, version string) (*CacheResult, error) {
|
|
upstreamURL, ok := h.repositories[repository]
|
|
if !ok {
|
|
return nil, h.unknownRepositoryError(repository)
|
|
}
|
|
index := h.fetchIndex(ctx, repository, upstreamURL)
|
|
if index.err != nil {
|
|
return nil, fmt.Errorf("fetching the %s index: %w", repository, index.err)
|
|
}
|
|
charts, err := h.chartLookup(repository, upstreamURL, index)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
release, ok := charts.byRelease[helmRelease{chart, version}]
|
|
switch {
|
|
case !ok:
|
|
return nil, fmt.Errorf("%w: %s %s is not in the %s index", ErrUpstreamNotFound, chart, version, repository)
|
|
case release.first.oci:
|
|
return nil, fmt.Errorf("%s %s is published as %s, which helm pulls through the container registry routes; mirror it as an image: %s",
|
|
chart, version, release.first.url, ociChartPURL(release.first.url, version))
|
|
case h.chartOnCooldown(chart, release.created):
|
|
return nil, fmt.Errorf("%s %s is in cooldown, so the index doesn't list it", chart, version)
|
|
}
|
|
return h.proxy.GetOrFetchArtifactFromURLWithDigest(ctx, helmMetadataEcosystem, repository, release.digest,
|
|
release.first.filename, release.first.url, "sha256:"+release.digest)
|
|
}
|
|
|
|
func (h *HelmHandler) unknownRepositoryError(repository string) error {
|
|
names := make([]string, 0, len(h.repositories))
|
|
for name := range h.repositories {
|
|
names = append(names, name)
|
|
}
|
|
if len(names) == 0 {
|
|
return fmt.Errorf("unknown Helm repository %q: upstream.helm configures none", repository)
|
|
}
|
|
slices.Sort(names)
|
|
return fmt.Errorf("unknown Helm repository %q: upstream.helm configures %s", repository, strings.Join(names, ", "))
|
|
}
|
|
|
|
// ociChartPURL returns the image PURL that mirrors an OCI chart reference,
|
|
// tagged with the chart version when the reference has no tag or digest, as
|
|
// helm pulls it.
|
|
func ociChartPURL(reference, version string) string {
|
|
repository := strings.TrimPrefix(reference, "oci://")
|
|
if at := strings.LastIndex(repository, "@"); at >= 0 {
|
|
repository, version = repository[:at], repository[at+1:]
|
|
} else if colon := strings.LastIndex(repository, ":"); colon > strings.LastIndex(repository, "/") {
|
|
repository, version = repository[:colon], repository[colon+1:]
|
|
}
|
|
return "pkg:oci/" + path.Base(repository) + "@" + version + "?repository_url=" + repository
|
|
}
|