Skip to content

Commit a6472c8

Browse files
fix: allow docker dhi helm charts to be used (cherry-pick #25835 for 3.3) (#25964)
Signed-off-by: Blake Pettersson <blake.pettersson@gmail.com> Co-authored-by: Blake Pettersson <blake.pettersson@gmail.com>
1 parent 74de77a commit a6472c8

File tree

2 files changed

+329
-19
lines changed

2 files changed

+329
-19
lines changed

util/oci/client.go

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ var (
4444
indexLock = sync.NewKeyLock()
4545
)
4646

47+
const (
48+
helmOCIConfigType = "application/vnd.cncf.helm.config.v1+json"
49+
helmOCILayerType = "application/vnd.cncf.helm.chart.content.v1.tar+gzip"
50+
)
51+
4752
var _ Client = &nativeOCIClient{}
4853

4954
type tagsCache interface {
@@ -260,6 +265,8 @@ func (c *nativeOCIClient) extract(ctx context.Context, digest string) (string, u
260265
return "", nil, err
261266
}
262267

268+
var isHelmChart bool
269+
263270
if !exists {
264271
ociManifest, err := getOCIManifest(ctx, digest, c.repo)
265272
if err != nil {
@@ -272,16 +279,25 @@ func (c *nativeOCIClient) extract(ctx context.Context, digest string) (string, u
272279
return "", nil, fmt.Errorf("expected no more than 10 oci layers, got %d", len(ociManifest.Layers))
273280
}
274281

282+
isHelmChart = ociManifest.Config.MediaType == helmOCIConfigType
283+
275284
contentLayers := 0
276285

277286
// Strictly speaking we only allow for a single content layer. There are images which contains extra layers, such
278287
// as provenance/attestation layers. Pending a better story to do this natively, we will skip such layers for now.
279288
for _, layer := range ociManifest.Layers {
280-
if isContentLayer(layer.MediaType) {
289+
// For Helm charts, only look for the specific Helm chart content layer
290+
if isHelmChart {
291+
if isHelmOCI(layer.MediaType) {
292+
if !slices.Contains(c.allowedMediaTypes, layer.MediaType) {
293+
return "", nil, fmt.Errorf("oci layer media type %s is not in the list of allowed media types", layer.MediaType)
294+
}
295+
contentLayers++
296+
}
297+
} else if isContentLayer(layer.MediaType) {
281298
if !slices.Contains(c.allowedMediaTypes, layer.MediaType) {
282299
return "", nil, fmt.Errorf("oci layer media type %s is not in the list of allowed media types", layer.MediaType)
283300
}
284-
285301
contentLayers++
286302
}
287303
}
@@ -301,7 +317,15 @@ func (c *nativeOCIClient) extract(ctx context.Context, digest string) (string, u
301317
maxSize = math.MaxInt64
302318
}
303319

304-
manifestsDir, err := extractContentToManifestsDir(ctx, cachedPath, digest, maxSize)
320+
if !isHelmChart {
321+
// Get the manifest to determine if it's a Helm chart for extraction
322+
ociManifest, err := getOCIManifestFromCache(ctx, cachedPath, digest)
323+
if err != nil {
324+
return "", nil, fmt.Errorf("error getting oci manifest for extraction: %w", err)
325+
}
326+
isHelmChart = ociManifest.Config.MediaType == helmOCIConfigType
327+
}
328+
manifestsDir, err := extractContentToManifestsDir(ctx, cachedPath, digest, maxSize, isHelmChart)
305329
if err != nil {
306330
return manifestsDir, nil, fmt.Errorf("cannot extract contents of oci image with revision %s: %w", digest, err)
307331
}
@@ -345,13 +369,7 @@ func (c *nativeOCIClient) digestMetadata(ctx context.Context, digest string) (*i
345369
if err != nil {
346370
return nil, fmt.Errorf("error fetching oci metadata path for digest %s: %w", digest, err)
347371
}
348-
349-
repo, err := oci.NewFromTar(ctx, path)
350-
if err != nil {
351-
return nil, fmt.Errorf("error extracting oci image for digest %s: %w", digest, err)
352-
}
353-
354-
return getOCIManifest(ctx, digest, repo)
372+
return getOCIManifestFromCache(ctx, path, digest)
355373
}
356374

357375
func (c *nativeOCIClient) ResolveRevision(ctx context.Context, revision string, noCache bool) (string, error) {
@@ -543,8 +561,8 @@ func saveCompressedImageToPath(ctx context.Context, digest string, repo oras.Rea
543561
}
544562

545563
// extractContentToManifestsDir looks up a locally stored OCI image, and extracts the embedded compressed layer which contains
546-
// K8s manifests to a temporary directory
547-
func extractContentToManifestsDir(ctx context.Context, cachedPath, digest string, maxSize int64) (string, error) {
564+
// K8s manifests to a temp dir.
565+
func extractContentToManifestsDir(ctx context.Context, cachedPath, digest string, maxSize int64, isHelmChart bool) (string, error) {
548566
manifestsDir, err := files.CreateTempDir(os.TempDir())
549567
if err != nil {
550568
return manifestsDir, err
@@ -561,7 +579,7 @@ func extractContentToManifestsDir(ctx context.Context, cachedPath, digest string
561579
}
562580
defer os.RemoveAll(tempDir)
563581

564-
fs, err := newCompressedLayerFileStore(manifestsDir, tempDir, maxSize)
582+
fs, err := newCompressedLayerFileStore(manifestsDir, tempDir, maxSize, isHelmChart)
565583
if err != nil {
566584
return manifestsDir, err
567585
}
@@ -574,26 +592,32 @@ func extractContentToManifestsDir(ctx context.Context, cachedPath, digest string
574592

575593
type compressedLayerExtracterStore struct {
576594
*file.Store
577-
dest string
578-
maxSize int64
595+
dest string
596+
maxSize int64
597+
isHelmChart bool
579598
}
580599

581-
func newCompressedLayerFileStore(dest, tempDir string, maxSize int64) (*compressedLayerExtracterStore, error) {
600+
func newCompressedLayerFileStore(dest, tempDir string, maxSize int64, isHelmChart bool) (*compressedLayerExtracterStore, error) {
582601
f, err := file.New(tempDir)
583602
if err != nil {
584603
return nil, err
585604
}
586605

587-
return &compressedLayerExtracterStore{f, dest, maxSize}, nil
606+
return &compressedLayerExtracterStore{f, dest, maxSize, isHelmChart}, nil
588607
}
589608

590609
func isHelmOCI(mediaType string) bool {
591-
return mediaType == "application/vnd.cncf.helm.chart.content.v1.tar+gzip"
610+
return mediaType == helmOCILayerType
592611
}
593612

594613
// Push looks in all the layers of an OCI image. Once it finds a layer that is compressed, it extracts the layer to a tempDir
595614
// and then renames the temp dir to the directory where the repo-server expects to find k8s manifests.
596615
func (s *compressedLayerExtracterStore) Push(ctx context.Context, desc imagev1.Descriptor, content io.Reader) error {
616+
// For Helm charts, only extract the Helm chart content layer, skip all other layers
617+
if s.isHelmChart && !isHelmOCI(desc.MediaType) {
618+
return s.Store.Push(ctx, desc, content)
619+
}
620+
597621
if isContentLayer(desc.MediaType) {
598622
srcDir, err := files.CreateTempDir(os.TempDir())
599623
if err != nil {
@@ -682,6 +706,15 @@ func getOCIManifest(ctx context.Context, digest string, repo oras.ReadOnlyTarget
682706
return &manifest, nil
683707
}
684708

709+
// getOCIManifestFromCache retrieves an OCI manifest from a cached tar file
710+
func getOCIManifestFromCache(ctx context.Context, cachedPath, digest string) (*imagev1.Manifest, error) {
711+
repo, err := oci.NewFromTar(ctx, cachedPath)
712+
if err != nil {
713+
return nil, fmt.Errorf("error creating oci store from cache: %w", err)
714+
}
715+
return getOCIManifest(ctx, digest, repo)
716+
}
717+
685718
// WithEventHandlers sets the git client event handlers
686719
func WithEventHandlers(handlers EventHandlers) ClientOpts {
687720
return func(c *nativeOCIClient) {

0 commit comments

Comments
 (0)