Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions image/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ import (

type config v1.Image

func findConfig(w walker, d *descriptor) (*config, error) {
func findConfig(w walker, d *v1.Descriptor) (*config, error) {
var c config
cpath := filepath.Join("blobs", d.algo(), d.hash())
cpath := filepath.Join("blobs", string(d.Digest.Algorithm()), d.Digest.Hex())

switch err := w.walk(func(path string, info os.FileInfo, r io.Reader) error {
if info.IsDir() || filepath.Clean(path) != cpath {
Expand Down
70 changes: 29 additions & 41 deletions image/descriptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,47 +20,29 @@ import (
"io"
"os"
"path/filepath"
"strings"

"github.com/opencontainers/go-digest"
"github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)

type descriptor struct {
MediaType string `json:"mediaType"`
Digest string `json:"digest"`
Size int64 `json:"size"`
}

func (d *descriptor) algo() string {
pts := strings.SplitN(d.Digest, ":", 2)
if len(pts) != 2 {
return ""
}
return pts[0]
}

func (d *descriptor) hash() string {
pts := strings.SplitN(d.Digest, ":", 2)
if len(pts) != 2 {
return ""
}
return pts[1]
}

func listReferences(w walker) (map[string]*descriptor, error) {
refs := make(map[string]*descriptor)
func listReferences(w walker) (map[string]*v1.Descriptor, error) {
refs := make(map[string]*v1.Descriptor)
var index v1.ImageIndex

if err := w.walk(func(path string, info os.FileInfo, r io.Reader) error {
if info.IsDir() || !strings.HasPrefix(path, "refs") {
if info.IsDir() || filepath.Clean(path) != "index.json" {
return nil
}

var d descriptor
if err := json.NewDecoder(r).Decode(&d); err != nil {
if err := json.NewDecoder(r).Decode(&index); err != nil {
return err
}
refs[info.Name()] = &d

for i := 0; i < len(index.Manifests); i++ {
if index.Manifests[i].Descriptor.Annotations["org.opencontainers.ref.name"] != "" {
refs[index.Manifests[i].Descriptor.Annotations["org.opencontainers.ref.name"]] = &index.Manifests[i].Descriptor
}
}

return nil
}); err != nil {
Expand All @@ -69,31 +51,38 @@ func listReferences(w walker) (map[string]*descriptor, error) {
return refs, nil
}

func findDescriptor(w walker, name string) (*descriptor, error) {
var d descriptor
dpath := filepath.Join("refs", name)
func findDescriptor(w walker, name string) (*v1.Descriptor, error) {
var d v1.Descriptor
var index v1.ImageIndex

switch err := w.walk(func(path string, info os.FileInfo, r io.Reader) error {
if info.IsDir() || filepath.Clean(path) != dpath {
if info.IsDir() || filepath.Clean(path) != "index.json" {
return nil
}

if err := json.NewDecoder(r).Decode(&d); err != nil {
if err := json.NewDecoder(r).Decode(&index); err != nil {
return err
}

return errEOW
for i := 0; i < len(index.Manifests); i++ {
if index.Manifests[i].Descriptor.Annotations["org.opencontainers.ref.name"] == name {
d = index.Manifests[i].Descriptor
return errEOW
}
}

return nil
}); err {
case nil:
return nil, fmt.Errorf("%s: descriptor not found", dpath)
return nil, fmt.Errorf("index.json: descriptor not found")
case errEOW:
return &d, nil
default:
return nil, err
}
}

func (d *descriptor) validate(w walker, mts []string) error {
func validateDescriptor(d *v1.Descriptor, w walker, mts []string) error {
var found bool
for _, mt := range mts {
if d.MediaType == mt {
Expand All @@ -105,13 +94,12 @@ func (d *descriptor) validate(w walker, mts []string) error {
return fmt.Errorf("invalid descriptor MediaType %q", d.MediaType)
}

parsed, err := digest.Parse(d.Digest)
if err != nil {
if err := d.Digest.Validate(); err != nil {
return err
}

// Copy the contents of the layer in to the verifier
verifier := parsed.Verifier()
verifier := d.Digest.Verifier()
numBytes, err := w.get(*d, verifier)
if err != nil {
return err
Expand Down
6 changes: 3 additions & 3 deletions image/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func validate(w walker, refs []string, out *log.Logger) error {
return fmt.Errorf("reference %s not found", ref)
}

if err = d.validate(w, validRefMediaTypes); err != nil {
if err = validateDescriptor(d, w, validRefMediaTypes); err != nil {
return err
}

Expand Down Expand Up @@ -135,7 +135,7 @@ func unpack(w walker, dest, refName string) error {
return err
}

if err = ref.validate(w, validRefMediaTypes); err != nil {
if err = validateDescriptor(ref, w, validRefMediaTypes); err != nil {
return err
}

Expand Down Expand Up @@ -183,7 +183,7 @@ func createRuntimeBundle(w walker, dest, refName, rootfs string) error {
return err
}

if err = ref.validate(w, validRefMediaTypes); err != nil {
if err = validateDescriptor(ref, w, validRefMediaTypes); err != nil {
return err
}

Expand Down
107 changes: 68 additions & 39 deletions image/image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ import (
)

const (
refTag = "latest"

refTag = "latest"
layoutStr = `{"imageLayoutVersion": "1.0.0"}`

configStr = `{
Expand Down Expand Up @@ -91,8 +90,44 @@ const (
)

var (
refStr = `{"digest":"<manifest_digest>","mediaType":"application/vnd.oci.image.manifest.v1+json","size":<manifest_size>}`

indexStr = `{
"schemaVersion": 2,
"manifests": [
{
"mediaType": "application/vnd.oci.image.index.v1+json",
"size": <manifest_size>,
"digest": "<manifest_digest>",
"annotations": {
"org.opencontainers.ref.name": "v1.0"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"size": <manifest_size>,
"digest": "<manifest_digest>",
"platform": {
"architecture": "ppc64le",
"os": "linux"
},
"annotations": {
"org.opencontainers.ref.name": "latest"
}
},
{
"mediaType": "application/xml",
"size": <manifest_size>,
"digest": "<manifest_digest>",
"annotations": {
"org.freedesktop.specifications.metainfo.version": "1.0",
"org.freedesktop.specifications.metainfo.type": "AppStream"
}
}
],
"annotations": {
"com.example.index.revision": "r124356"
}
}
`
manifestStr = `{
"annotations": null,
"config": {
Expand Down Expand Up @@ -162,11 +197,6 @@ func createImageLayoutBundle(il imageLayout) error {
return err
}

err = os.MkdirAll(filepath.Join(il.rootDir, "refs"), 0700)
if err != nil {
return err
}

// create image layout file
err = createLayoutFile(il.rootDir)
if err != nil {
Expand All @@ -178,14 +208,14 @@ func createImageLayoutBundle(il imageLayout) error {
if err != nil {
return err
}
il.manifest = strings.Replace(il.manifest, "<layer_digest>", desc.Digest, 1)
il.manifest = strings.Replace(il.manifest, "<layer_digest>", string(desc.Digest), 1)
il.manifest = strings.Replace(il.manifest, "<layer_size>", strconv.FormatInt(desc.Size, 10), 1)

desc, err = createConfigFile(il.rootDir, il.config)
if err != nil {
return err
}
il.manifest = strings.Replace(il.manifest, "<config_digest>", desc.Digest, 1)
il.manifest = strings.Replace(il.manifest, "<config_digest>", string(desc.Digest), 1)
il.manifest = strings.Replace(il.manifest, "<config_size>", strconv.FormatInt(desc.Size, 10), 1)

// create manifest blob file
Expand All @@ -194,7 +224,7 @@ func createImageLayoutBundle(il imageLayout) error {
return err
}

return createRefFile(il.rootDir, il.ref, desc)
return createIndexFile(il.rootDir, desc)
}

func createLayoutFile(root string) error {
Expand All @@ -208,61 +238,61 @@ func createLayoutFile(root string) error {
return err
}

func createRefFile(root, ref string, mft descriptor) error {
refpath := filepath.Join(root, "refs", ref)
f, err := os.Create(refpath)
func createIndexFile(root string, mft v1.Descriptor) error {
indexpath := filepath.Join(root, "index.json")
f, err := os.Create(indexpath)
if err != nil {
return err
}
defer f.Close()
refStr = strings.Replace(refStr, "<manifest_digest>", mft.Digest, -1)
refStr = strings.Replace(refStr, "<manifest_size>", strconv.FormatInt(mft.Size, 10), -1)
_, err = io.Copy(f, bytes.NewBuffer([]byte(refStr)))
indexStr = strings.Replace(indexStr, "<manifest_digest>", string(mft.Digest), -1)
indexStr = strings.Replace(indexStr, "<manifest_size>", strconv.FormatInt(mft.Size, 10), -1)
_, err = io.Copy(f, bytes.NewBuffer([]byte(indexStr)))
return err
}

func createManifestFile(root, str string) (descriptor, error) {
func createManifestFile(root, str string) (v1.Descriptor, error) {
name := filepath.Join(root, "blobs", "sha256", "test-manifest")
f, err := os.Create(name)
if err != nil {
return descriptor{}, err
return v1.Descriptor{}, err
}
defer f.Close()

_, err = io.Copy(f, bytes.NewBuffer([]byte(str)))
if err != nil {
return descriptor{}, err
return v1.Descriptor{}, err
}

return createHashedBlob(name)
}

func createConfigFile(root, config string) (descriptor, error) {
func createConfigFile(root, config string) (v1.Descriptor, error) {
name := filepath.Join(root, "blobs", "sha256", "test-config")
f, err := os.Create(name)
if err != nil {
return descriptor{}, err
return v1.Descriptor{}, err
}
defer f.Close()

_, err = io.Copy(f, bytes.NewBuffer([]byte(config)))
if err != nil {
return descriptor{}, err
return v1.Descriptor{}, err
}

return createHashedBlob(name)
}

func createImageLayerFile(root string, list []tarContent) (descriptor, error) {
func createImageLayerFile(root string, list []tarContent) (v1.Descriptor, error) {
name := filepath.Join(root, "blobs", "sha256", "test-layer")
err := createTarBlob(name, list)
if err != nil {
return descriptor{}, err
return v1.Descriptor{}, err
}

desc, err := createHashedBlob(name)
if err != nil {
return descriptor{}, err
return v1.Descriptor{}, err
}

desc.MediaType = v1.MediaTypeImageLayer
Expand Down Expand Up @@ -291,41 +321,40 @@ func createTarBlob(name string, list []tarContent) error {
return nil
}

func createHashedBlob(name string) (descriptor, error) {
func createHashedBlob(name string) (v1.Descriptor, error) {
desc, err := newDescriptor(name)
if err != nil {
return descriptor{}, err
return v1.Descriptor{}, err
}

parsed, err := digest.Parse(desc.Digest)
if err != nil {
return descriptor{}, err
if err := desc.Digest.Validate(); err != nil {
return v1.Descriptor{}, err
}

// Rename the file to hashed-digest name.
err = os.Rename(name, filepath.Join(filepath.Dir(name), parsed.Hex()))
err = os.Rename(name, filepath.Join(filepath.Dir(name), desc.Digest.Hex()))
if err != nil {
return descriptor{}, err
return v1.Descriptor{}, err
}

return desc, nil
}

func newDescriptor(name string) (descriptor, error) {
func newDescriptor(name string) (v1.Descriptor, error) {
file, err := os.Open(name)
if err != nil {
return descriptor{}, err
return v1.Descriptor{}, err
}
defer file.Close()

digester := digest.SHA256.Digester()
size, err := io.Copy(digester.Hash(), file)
if err != nil {
return descriptor{}, err
return v1.Descriptor{}, err
}

return descriptor{
Digest: digester.Digest().String(),
return v1.Descriptor{
Digest: digester.Digest(),
Size: size,
}, nil
}
Loading