fix: make ORAS reference private

Signed-off-by: Terry Howe <terrylhowe@gmail.com>
pull/12690/head
Terry Howe 11 months ago
parent aca7e8d775
commit d2b94f6200

@ -23,7 +23,6 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/Masterminds/semver/v3"
"github.com/pkg/errors" "github.com/pkg/errors"
"helm.sh/helm/v3/internal/fileutil" "helm.sh/helm/v3/internal/fileutil"
@ -141,71 +140,6 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven
return destfile, ver, nil return destfile, ver, nil
} }
func (c *ChartDownloader) getOciURI(ref, version string, u *url.URL) (*url.URL, error) {
var tag string
registryReference, err := registry.NewReference(u.Path)
if err != nil {
return nil, err
}
if version == "" {
// Use OCI URI tag as default
version = registryReference.Tag
} else {
if registryReference.Tag != "" && registryReference.Tag != version {
return nil, errors.Errorf("chart reference and version mismatch: %s is not %s", version, registryReference.Tag)
}
}
if registryReference.Digest != "" {
if registryReference.Tag == "" {
// Install by digest only
return u, nil
}
// Validate the tag if it was specified
path := registryReference.Registry + "/" + registryReference.Repository + ":" + registryReference.Tag
desc, err := c.RegistryClient.Resolve(path)
if err != nil {
// The resource does not have to be tagged when digest is specified
return u, nil
}
if desc != nil && desc.Digest.String() != registryReference.Digest {
return nil, errors.Errorf("chart reference digest mismatch: %s is not %s", desc.Digest.String(), registryReference.Digest)
}
return u, nil
}
// Evaluate whether an explicit version has been provided. Otherwise, determine version to use
_, errSemVer := semver.NewVersion(version)
if errSemVer == nil {
tag = version
} else {
// Retrieve list of repository tags
tags, err := c.RegistryClient.Tags(strings.TrimPrefix(ref, fmt.Sprintf("%s://", registry.OCIScheme)))
if err != nil {
return nil, err
}
if len(tags) == 0 {
return nil, errors.Errorf("Unable to locate any tags in provided repository: %s", ref)
}
// Determine if version provided
// If empty, try to get the highest available tag
// If exact version, try to find it
// If semver constraint string, try to find a match
tag, err = registry.GetTagMatchingVersionOrConstraint(tags, version)
if err != nil {
return nil, err
}
}
u.Path = fmt.Sprintf("%s/%s:%s", registryReference.Registry, registryReference.Repository, tag)
return u, err
}
// ResolveChartVersion resolves a chart reference to a URL. // ResolveChartVersion resolves a chart reference to a URL.
// //
// It returns the URL and sets the ChartDownloader's Options that can fetch // It returns the URL and sets the ChartDownloader's Options that can fetch
@ -228,7 +162,7 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er
} }
if registry.IsOCI(u.String()) { if registry.IsOCI(u.String()) {
return c.getOciURI(ref, version, u) return c.RegistryClient.ValidateReference(ref, version, u)
} }
rf, err := loadRepoConfig(c.RepositoryConfig) rf, err := loadRepoConfig(c.RepositoryConfig)

@ -22,6 +22,7 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"net/url"
"sort" "sort"
"strings" "strings"
@ -319,7 +320,7 @@ type (
// Pull downloads a chart from a registry // Pull downloads a chart from a registry
func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) { func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) {
parsedRef, err := NewReference(ref) parsedRef, err := newReference(ref)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -351,7 +352,7 @@ func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) {
} }
var descriptors, layers []ocispec.Descriptor var descriptors, layers []ocispec.Descriptor
remotesResolver, err := c.resolver(parsedRef.OrasReference) remotesResolver, err := c.resolver(parsedRef.orasReference)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -535,7 +536,7 @@ type (
// Push uploads a chart to a registry. // Push uploads a chart to a registry.
func (c *Client) Push(data []byte, ref string, options ...PushOption) (*PushResult, error) { func (c *Client) Push(data []byte, ref string, options ...PushOption) (*PushResult, error) {
parsedRef, err := NewReference(ref) parsedRef, err := newReference(ref)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -594,12 +595,12 @@ func (c *Client) Push(data []byte, ref string, options ...PushOption) (*PushResu
return nil, err return nil, err
} }
remotesResolver, err := c.resolver(parsedRef.OrasReference) remotesResolver, err := c.resolver(parsedRef.orasReference)
if err != nil { if err != nil {
return nil, err return nil, err
} }
registryStore := content.Registry{Resolver: remotesResolver} registryStore := content.Registry{Resolver: remotesResolver}
_, err = oras.Copy(ctx(c.out, c.debug), memoryStore, parsedRef.OrasReference.String(), registryStore, "", _, err = oras.Copy(ctx(c.out, c.debug), memoryStore, parsedRef.orasReference.String(), registryStore, "",
oras.WithNameValidation(nil)) oras.WithNameValidation(nil))
if err != nil { if err != nil {
return nil, err return nil, err
@ -630,7 +631,7 @@ func (c *Client) Push(data []byte, ref string, options ...PushOption) (*PushResu
} }
fmt.Fprintf(c.out, "Pushed: %s\n", result.Ref) fmt.Fprintf(c.out, "Pushed: %s\n", result.Ref)
fmt.Fprintf(c.out, "Digest: %s\n", result.Manifest.Digest) fmt.Fprintf(c.out, "Digest: %s\n", result.Manifest.Digest)
if strings.Contains(parsedRef.OrasReference.Reference, "_") { if strings.Contains(parsedRef.orasReference.Reference, "_") {
fmt.Fprintf(c.out, "%s contains an underscore.\n", result.Ref) fmt.Fprintf(c.out, "%s contains an underscore.\n", result.Ref)
fmt.Fprint(c.out, registryUnderscoreMessage+"\n") fmt.Fprint(c.out, registryUnderscoreMessage+"\n")
} }
@ -705,7 +706,7 @@ func (c *Client) Tags(ref string) ([]string, error) {
// Resolve a reference to a descriptor. // Resolve a reference to a descriptor.
func (c *Client) Resolve(ref string) (*ocispec.Descriptor, error) { func (c *Client) Resolve(ref string) (*ocispec.Descriptor, error) {
ctx := context.Background() ctx := context.Background()
parsedRef, err := NewReference(ref) parsedRef, err := newReference(ref)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -713,7 +714,7 @@ func (c *Client) Resolve(ref string) (*ocispec.Descriptor, error) {
return nil, nil return nil, nil
} }
remotesResolver, err := c.resolver(parsedRef.OrasReference) remotesResolver, err := c.resolver(parsedRef.orasReference)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -721,3 +722,69 @@ func (c *Client) Resolve(ref string) (*ocispec.Descriptor, error) {
_, desc, err := remotesResolver.Resolve(ctx, ref) _, desc, err := remotesResolver.Resolve(ctx, ref)
return &desc, err return &desc, err
} }
// ValidateReference for path and version
func (c *Client) ValidateReference(ref, version string, u *url.URL) (*url.URL, error) {
var tag string
registryReference, err := newReference(u.Path)
if err != nil {
return nil, err
}
if version == "" {
// Use OCI URI tag as default
version = registryReference.Tag
} else {
if registryReference.Tag != "" && registryReference.Tag != version {
return nil, errors.Errorf("chart reference and version mismatch: %s is not %s", version, registryReference.Tag)
}
}
if registryReference.Digest != "" {
if registryReference.Tag == "" {
// Install by digest only
return u, nil
}
// Validate the tag if it was specified
path := registryReference.Registry + "/" + registryReference.Repository + ":" + registryReference.Tag
desc, err := c.Resolve(path)
if err != nil {
// The resource does not have to be tagged when digest is specified
return u, nil
}
if desc != nil && desc.Digest.String() != registryReference.Digest {
return nil, errors.Errorf("chart reference digest mismatch: %s is not %s", desc.Digest.String(), registryReference.Digest)
}
return u, nil
}
// Evaluate whether an explicit version has been provided. Otherwise, determine version to use
_, errSemVer := semver.NewVersion(version)
if errSemVer == nil {
tag = version
} else {
// Retrieve list of repository tags
tags, err := c.Tags(strings.TrimPrefix(ref, fmt.Sprintf("%s://", OCIScheme)))
if err != nil {
return nil, err
}
if len(tags) == 0 {
return nil, errors.Errorf("Unable to locate any tags in provided repository: %s", ref)
}
// Determine if version provided
// If empty, try to get the highest available tag
// If exact version, try to find it
// If semver constraint string, try to find a match
tag, err = GetTagMatchingVersionOrConstraint(tags, version)
if err != nil {
return nil, err
}
}
u.Path = fmt.Sprintf("%s/%s:%s", registryReference.Registry, registryReference.Repository, tag)
return u, err
}

@ -22,19 +22,19 @@ import (
orasregistry "oras.land/oras-go/pkg/registry" orasregistry "oras.land/oras-go/pkg/registry"
) )
type Reference struct { type reference struct {
OrasReference orasregistry.Reference orasReference orasregistry.Reference
Registry string Registry string
Repository string Repository string
Tag string Tag string
Digest string Digest string
} }
// NewReference will parse and validate the reference, and clean tags when // newReference will parse and validate the reference, and clean tags when
// applicable tags are only cleaned when plus (+) signs are present, and are // applicable tags are only cleaned when plus (+) signs are present, and are
// converted to underscores (_) before pushing // converted to underscores (_) before pushing
// See https://github.com/helm/helm/issues/10166 // See https://github.com/helm/helm/issues/10166
func NewReference(raw string) (result Reference, err error) { func newReference(raw string) (result reference, err error) {
// Remove oci:// prefix if it is there // Remove oci:// prefix if it is there
raw = strings.TrimPrefix(raw, OCIScheme+"://") raw = strings.TrimPrefix(raw, OCIScheme+"://")
@ -60,19 +60,19 @@ func NewReference(raw string) (result Reference, err error) {
} }
} }
result.OrasReference, err = orasregistry.ParseReference(raw) result.orasReference, err = orasregistry.ParseReference(raw)
if err != nil { if err != nil {
return result, err return result, err
} }
result.Registry = result.OrasReference.Registry result.Registry = result.orasReference.Registry
result.Repository = result.OrasReference.Repository result.Repository = result.orasReference.Repository
result.Tag = result.OrasReference.Reference result.Tag = result.orasReference.Reference
return result, nil return result, nil
} }
func (r *Reference) String() string { func (r *reference) String() string {
if r.Tag == "" { if r.Tag == "" {
return r.OrasReference.String() + "@" + r.Digest return r.orasReference.String() + "@" + r.Digest
} }
return r.OrasReference.String() return r.orasReference.String()
} }

@ -18,15 +18,15 @@ package registry
import "testing" import "testing"
func verify(t *testing.T, actual Reference, registry, repository, tag, digest string) { func verify(t *testing.T, actual reference, registry, repository, tag, digest string) {
if registry != actual.OrasReference.Registry { if registry != actual.orasReference.Registry {
t.Errorf("Oras Reference registry expected %v actual %v", registry, actual.Registry) t.Errorf("Oras reference registry expected %v actual %v", registry, actual.Registry)
} }
if repository != actual.OrasReference.Repository { if repository != actual.orasReference.Repository {
t.Errorf("Oras Reference repository expected %v actual %v", repository, actual.Repository) t.Errorf("Oras reference repository expected %v actual %v", repository, actual.Repository)
} }
if tag != actual.OrasReference.Reference { if tag != actual.orasReference.Reference {
t.Errorf("Oras Reference reference expected %v actual %v", tag, actual.Tag) t.Errorf("Oras reference reference expected %v actual %v", tag, actual.Tag)
} }
if registry != actual.Registry { if registry != actual.Registry {
t.Errorf("Registry expected %v actual %v", registry, actual.Registry) t.Errorf("Registry expected %v actual %v", registry, actual.Registry)
@ -55,43 +55,43 @@ func verify(t *testing.T, actual Reference, registry, repository, tag, digest st
} }
func TestNewReference(t *testing.T) { func TestNewReference(t *testing.T) {
actual, err := NewReference("registry.example.com/repository:1.0@sha256:c6841b3a895f1444a6738b5d04564a57e860ce42f8519c3be807fb6d9bee7888") actual, err := newReference("registry.example.com/repository:1.0@sha256:c6841b3a895f1444a6738b5d04564a57e860ce42f8519c3be807fb6d9bee7888")
if err != nil { if err != nil {
t.Errorf("Unexpected error %v", err) t.Errorf("Unexpected error %v", err)
} }
verify(t, actual, "registry.example.com", "repository", "1.0", "sha256:c6841b3a895f1444a6738b5d04564a57e860ce42f8519c3be807fb6d9bee7888") verify(t, actual, "registry.example.com", "repository", "1.0", "sha256:c6841b3a895f1444a6738b5d04564a57e860ce42f8519c3be807fb6d9bee7888")
actual, err = NewReference("oci://registry.example.com/repository:1.0@sha256:c6841b3a895f1444a6738b5d04564a57e860ce42f8519c3be807fb6d9bee7888") actual, err = newReference("oci://registry.example.com/repository:1.0@sha256:c6841b3a895f1444a6738b5d04564a57e860ce42f8519c3be807fb6d9bee7888")
if err != nil { if err != nil {
t.Errorf("Unexpected error %v", err) t.Errorf("Unexpected error %v", err)
} }
verify(t, actual, "registry.example.com", "repository", "1.0", "sha256:c6841b3a895f1444a6738b5d04564a57e860ce42f8519c3be807fb6d9bee7888") verify(t, actual, "registry.example.com", "repository", "1.0", "sha256:c6841b3a895f1444a6738b5d04564a57e860ce42f8519c3be807fb6d9bee7888")
actual, err = NewReference("a/b:1@c") actual, err = newReference("a/b:1@c")
if err != nil { if err != nil {
t.Errorf("Unexpected error %v", err) t.Errorf("Unexpected error %v", err)
} }
verify(t, actual, "a", "b", "1", "c") verify(t, actual, "a", "b", "1", "c")
actual, err = NewReference("a/b:@") actual, err = newReference("a/b:@")
if err != nil { if err != nil {
t.Errorf("Unexpected error %v", err) t.Errorf("Unexpected error %v", err)
} }
verify(t, actual, "a", "b", "", "") verify(t, actual, "a", "b", "", "")
actual, err = NewReference("registry.example.com/repository:1.0+001") actual, err = newReference("registry.example.com/repository:1.0+001")
if err != nil { if err != nil {
t.Errorf("Unexpected error %v", err) t.Errorf("Unexpected error %v", err)
} }
verify(t, actual, "registry.example.com", "repository", "1.0_001", "") verify(t, actual, "registry.example.com", "repository", "1.0_001", "")
actual, err = NewReference("thing:1.0") actual, err = newReference("thing:1.0")
if err == nil { if err == nil {
t.Errorf("Expect error error %v", err) t.Errorf("Expect error error %v", err)
} }
verify(t, actual, "", "", "", "") verify(t, actual, "", "", "", "")
actual, err = NewReference("registry.example.com/the/repository@sha256:c6841b3a895f1444a6738b5d04564a57e860ce42f8519c3be807fb6d9bee7888") actual, err = newReference("registry.example.com/the/repository@sha256:c6841b3a895f1444a6738b5d04564a57e860ce42f8519c3be807fb6d9bee7888")
if err != nil { if err != nil {
t.Errorf("Unexpected error %v", err) t.Errorf("Unexpected error %v", err)
} }

Loading…
Cancel
Save