1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
|
// Copyright 2023 Sylabs Inc. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0
package sif
import (
"bytes"
"encoding/json"
"errors"
"fmt"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/types"
"github.com/sylabs/sif/v2/pkg/sif"
)
var _ v1.ImageIndex = (*imageIndex)(nil)
// ImageIndexFromFileImage returns a v1.ImageIndex corresponding to f.
func ImageIndexFromFileImage(fi *sif.FileImage) (v1.ImageIndex, error) {
f := &fileImage{fi}
return f.ImageIndex()
}
type imageIndex struct {
f *fileImage
desc *v1.Descriptor
rawManifest []byte
}
// ImageIndex returns a v1.ImageIndex from f.
func (f *fileImage) ImageIndex() (v1.ImageIndex, error) {
d, err := f.GetDescriptor(
sif.WithDataType(sif.DataOCIRootIndex),
)
if err != nil {
return nil, err
}
b, err := d.GetData()
if err != nil {
return nil, err
}
digest, size, err := v1.SHA256(bytes.NewReader(b))
if err != nil {
return nil, err
}
return &imageIndex{
f: f,
desc: &v1.Descriptor{
MediaType: types.OCIImageIndex,
Size: size,
Digest: digest,
},
rawManifest: b,
}, nil
}
// MediaType of this image's manifest.
func (ix *imageIndex) MediaType() (types.MediaType, error) {
return ix.desc.MediaType, nil
}
// Digest returns the sha256 of this index's manifest.
func (ix *imageIndex) Digest() (v1.Hash, error) {
return ix.desc.Digest, nil
}
// Size returns the size of the manifest.
func (ix *imageIndex) Size() (int64, error) {
return ix.desc.Size, nil
}
// IndexManifest returns this image index's manifest object.
func (ix *imageIndex) IndexManifest() (*v1.IndexManifest, error) {
var im v1.IndexManifest
err := json.Unmarshal(ix.rawManifest, &im)
return &im, err
}
// RawManifest returns the serialized bytes of IndexManifest().
func (ix *imageIndex) RawManifest() ([]byte, error) {
return ix.rawManifest, nil
}
// Descriptor returns the original descriptor from an index manifest. See partial.Descriptor.
func (ix *imageIndex) Descriptor() (*v1.Descriptor, error) {
return ix.desc, nil
}
var errUnexpectedMediaType = errors.New("unexpected media type")
// Image returns a v1.Image that this ImageIndex references.
func (ix *imageIndex) Image(h v1.Hash) (v1.Image, error) {
desc, err := ix.findDescriptor(h)
if err != nil {
return nil, err
}
if mt := desc.MediaType; !mt.IsImage() {
return nil, fmt.Errorf("%w for %v: %v", errUnexpectedMediaType, h, desc.MediaType)
}
b, err := ix.f.Bytes(h)
if err != nil {
return nil, err
}
img := image{
f: ix.f,
desc: desc,
rawManifest: b,
}
return &img, nil
}
// ImageIndex returns a v1.ImageIndex that this ImageIndex references.
func (ix *imageIndex) ImageIndex(h v1.Hash) (v1.ImageIndex, error) {
desc, err := ix.findDescriptor(h)
if err != nil {
return nil, err
}
if mt := desc.MediaType; !mt.IsIndex() {
return nil, fmt.Errorf("%w for %v: %v", errUnexpectedMediaType, h, desc.MediaType)
}
b, err := ix.f.Bytes(h)
if err != nil {
return nil, err
}
return &imageIndex{
f: ix.f,
desc: desc,
rawManifest: b,
}, nil
}
var errDescriptorNotFoundInIndex = errors.New("descriptor not found in index")
// findDescriptor returns the descriptor with the supplied digest.
func (ix *imageIndex) findDescriptor(h v1.Hash) (*v1.Descriptor, error) {
im, err := ix.IndexManifest()
if err != nil {
return nil, err
}
for _, desc := range im.Manifests {
if desc.Digest == h {
return &desc, nil
}
}
return nil, fmt.Errorf("%w: %s", errDescriptorNotFoundInIndex, h)
}
|