pull/17084/merge
George Jenkins 5 months ago committed by GitHub
commit bfb0de5411
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -6,6 +6,30 @@
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/3131/badge)](https://bestpractices.coreinfrastructure.org/projects/3131)
[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/helm/helm/badge)](https://scorecard.dev/viewer/?uri=github.com/helm/helm)
## PR
Run a registry:
```
docker run -d -p 5000:5000 --restart always --name registry docker.io/registry:3.0.0-rc.2
```
Create a chart and values, push values to registry:
```
helm create oci-values-demo
$ cat value-overides.yaml
---
replicaCount: 3
$ oras push localhost:5000/oci-values-demo-values:demo value-overides.yaml:application/vnd.cncf.helm.values.v1
```
Install the chart, pulling values from OCI:
```
make && ./bin/helm upgrade --install oci-values-demo-release oci-values-demo/ -f oci://localhost:5000/oci-values-demo-values:demo
```
Helm is a tool for managing Charts. Charts are packages of pre-configured Kubernetes resources.
Use Helm to:

@ -131,7 +131,8 @@ func readFile(filePath string, p getter.Providers) ([]byte, error) {
if err != nil {
return os.ReadFile(filePath)
}
data, err := g.Get(filePath, getter.WithURL(filePath))
data, err := g.Get(filePath, getter.WithURL(filePath), getter.WithTarget("values"))
if err != nil {
return nil, err
}

@ -47,6 +47,7 @@ type options struct {
registryClient *registry.Client
timeout time.Duration
transport *http.Transport
target string
}
// Option allows specifying various settings configurable by the user for overriding the defaults
@ -143,6 +144,13 @@ func WithTransport(transport *http.Transport) Option {
}
}
// WithTransport sets the http.Transport to allow overwriting the HTTPGetter default.
func WithTarget(target string) Option {
return func(opts *options) {
opts.target = target
}
}
// Getter is an interface to support GET to the specified URL.
type Getter interface {
// Get file content by url string

@ -39,6 +39,7 @@ type OCIGetter struct {
// Get performs a Get from repo.Getter and returns the body.
func (g *OCIGetter) Get(href string, options ...Option) (*bytes.Buffer, error) {
g.opts.target = "chart"
for _, opt := range options {
opt(&g.opts)
}
@ -76,12 +77,19 @@ func (g *OCIGetter) get(href string) (*bytes.Buffer, error) {
return nil, err
}
switch g.opts.target {
case "values":
return bytes.NewBuffer(result.Values.Data), nil
case "chart":
if requestingProv {
return bytes.NewBuffer(result.Prov.Data), nil
}
return bytes.NewBuffer(result.Chart.Data), nil
}
return nil, fmt.Errorf("unknown get target: %s", g.opts.target)
}
// NewOCIGetter constructs a valid http/https client as a Getter
func NewOCIGetter(ops ...Option) (Getter, error) {
var client OCIGetter

@ -390,6 +390,7 @@ type (
Chart *DescriptorPullSummaryWithMeta `json:"chart"`
Prov *DescriptorPullSummary `json:"prov"`
Ref string `json:"ref"`
Values *DescriptorPullSummary `json:"values"`
}
DescriptorPullSummary struct {
@ -427,24 +428,23 @@ func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) {
return nil, errors.New(
"must specify at least one layer to pull (chart/prov)")
}
memoryStore := memory.New()
allowedMediaTypes := []string{
ocispec.MediaTypeImageManifest,
ConfigMediaType,
}
minNumDescriptors := 1 // 1 for the config
if operation.withChart {
minNumDescriptors++
allowedMediaTypes = append(allowedMediaTypes, ChartLayerMediaType, LegacyChartLayerMediaType)
}
if operation.withProv {
if !operation.ignoreMissingProv {
minNumDescriptors++
}
allowedMediaTypes = append(allowedMediaTypes, ProvLayerMediaType)
}
var descriptors, layers []ocispec.Descriptor
memoryStore := memory.New()
//allowedMediaTypes := []string{
// ocispec.MediaTypeImageManifest,
// ConfigMediaType,
//}
//minNumDescriptors := 1 // 1 for the config
//if operation.withChart {
// minNumDescriptors++
// allowedMediaTypes = append(allowedMediaTypes, ChartLayerMediaType, LegacyChartLayerMediaType)
//}
//if operation.withProv {
// if !operation.ignoreMissingProv {
// minNumDescriptors++
// }
// allowedMediaTypes = append(allowedMediaTypes, ProvLayerMediaType)
//}
repository, err := remote.NewRepository(parsedRef.String())
if err != nil {
@ -455,16 +455,17 @@ func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) {
ctx := context.Background()
sort.Strings(allowedMediaTypes)
//sort.Strings(allowedMediaTypes)
var mu sync.Mutex
var layers []ocispec.Descriptor
manifest, err := oras.Copy(ctx, repository, parsedRef.String(), memoryStore, "", oras.CopyOptions{
CopyGraphOptions: oras.CopyGraphOptions{
PreCopy: func(_ context.Context, desc ocispec.Descriptor) error {
mediaType := desc.MediaType
if i := sort.SearchStrings(allowedMediaTypes, mediaType); i >= len(allowedMediaTypes) || allowedMediaTypes[i] != mediaType {
return errors.Errorf("media type %q is not allowed, found in descriptor with digest: %q", mediaType, desc.Digest)
}
//mediaType := desc.MediaType
//if i := sort.SearchStrings(allowedMediaTypes, mediaType); i >= len(allowedMediaTypes) || allowedMediaTypes[i] != mediaType {
// return errors.Errorf("media type %q is not allowed, found in descriptor with digest: %q", mediaType, desc.Digest)
//}
mu.Lock()
layers = append(layers, desc)
@ -477,17 +478,19 @@ func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) {
return nil, err
}
var descriptors []ocispec.Descriptor
descriptors = append(descriptors, manifest)
descriptors = append(descriptors, layers...)
numDescriptors := len(descriptors)
if numDescriptors < minNumDescriptors {
return nil, fmt.Errorf("manifest does not contain minimum number of descriptors (%d), descriptors found: %d",
minNumDescriptors, numDescriptors)
if manifest.MediaType != "application/vnd.oci.image.manifest.v1+json" {
return nil, fmt.Errorf("unexpected reference mediatype: %s", manifest.MediaType)
}
var configDescriptor *ocispec.Descriptor
var chartDescriptor *ocispec.Descriptor
var provDescriptor *ocispec.Descriptor
var valuesDescriptor *ocispec.Descriptor
for _, descriptor := range descriptors {
d := descriptor
switch d.MediaType {
@ -497,11 +500,30 @@ func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) {
chartDescriptor = &d
case ProvLayerMediaType:
provDescriptor = &d
case ValuesMediaType:
valuesDescriptor = &d
case LegacyChartLayerMediaType:
chartDescriptor = &d
fmt.Fprintf(c.out, "Warning: chart media type %s is deprecated\n", LegacyChartLayerMediaType)
}
}
if valuesDescriptor != nil {
data, err := content.FetchAll(ctx, memoryStore, *valuesDescriptor)
if err != nil {
return nil, fmt.Errorf("unable to retrieve blob with digest %s: %w", valuesDescriptor.Digest, err)
}
return &PullResult{
Ref: parsedRef.String(),
Values: &DescriptorPullSummary{
Digest: valuesDescriptor.Digest.String(),
Size: valuesDescriptor.Size,
Data: data,
},
}, nil
}
if configDescriptor == nil {
return nil, fmt.Errorf("could not load config with mediatype %s", ConfigMediaType)
}
@ -530,6 +552,7 @@ func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) {
Chart: &DescriptorPullSummaryWithMeta{},
Prov: &DescriptorPullSummary{},
Ref: parsedRef.String(),
Values: nil,
}
result.Manifest.Data, err = content.FetchAll(ctx, memoryStore, manifest)

@ -32,6 +32,9 @@ const (
// ProvLayerMediaType is the reserved media type for Helm chart provenance files
ProvLayerMediaType = "application/vnd.cncf.helm.chart.provenance.v1.prov"
// ValuesMediaType is the reserved media type for the Helm chart values
ValuesMediaType = "application/vnd.cncf.helm.values.v1"
// LegacyChartLayerMediaType is the legacy reserved media type for Helm chart package content.
LegacyChartLayerMediaType = "application/tar+gzip"
)

Loading…
Cancel
Save