Skip to content

Commit

Permalink
Use ImageIndex to store image related signatures etc
Browse files Browse the repository at this point in the history
Signed-off-by: Priya Wadhwa <priyawadhwa@google.com>
  • Loading branch information
Priya Wadhwa committed Nov 16, 2021
1 parent d1b6d07 commit f8ffe71
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 129 deletions.
101 changes: 0 additions & 101 deletions pkg/oci/layout/image.go

This file was deleted.

113 changes: 113 additions & 0 deletions pkg/oci/layout/index.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//
// Copyright 2021 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package layout

import (
"fmt"

v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/layout"
"github.com/sigstore/cosign/pkg/oci"
"github.com/sigstore/cosign/pkg/oci/signed"
)

const (
imageAnnotation = "dev.cosignproject.cosign/image"
sigsAnnotation = "dev.cosignproject.cosign/sigs"
)

// SignedImageIndex provides access to a local index reference, and its signatures.
func SignedImageIndex(path string) (oci.SignedImageIndex, error) {
p, err := layout.FromPath(path)
if err != nil {
return nil, err
}
ii, err := p.ImageIndex()
if err != nil {
return nil, err
}
return &index{
v1Index: ii,
}, nil
}

// We alias ImageIndex so that we can inline it without the type
// name colliding with the name of a method it had to implement.
type v1Index v1.ImageIndex

type index struct {
v1Index
}

var _ oci.SignedImageIndex = (*index)(nil)

// Signatures implements oci.SignedImageIndex
func (i *index) Signatures() (oci.Signatures, error) {
sigsImage, err := i.imageByAnnotation(sigsAnnotation)
if err != nil {
return nil, err
}
return &sigs{sigsImage}, nil
}

// Attestations implements oci.SignedImageIndex
func (i *index) Attestations() (oci.Signatures, error) {
return nil, fmt.Errorf("not yet implemented")
}

// Attestations implements oci.SignedImage
func (i *index) Attachment(name string) (oci.File, error) {
return nil, fmt.Errorf("not yet implemented")
}

// SignedImage implements oci.SignedImageIndex
func (i *index) SignedImage(h v1.Hash) (oci.SignedImage, error) {
img, err := i.Image(h)
if err != nil {
return nil, err
}
return signed.Image(img), nil
}

// imageByAnnotation searches through all manifests in the index.json
// and returns the image that has the matching annotation
func (i *index) imageByAnnotation(annotation string) (v1.Image, error) {
manifest, err := i.IndexManifest()
if err != nil {
return nil, err
}
for _, m := range manifest.Manifests {
if _, ok := m.Annotations[annotation]; ok {
img, err := i.Image(m.Digest)
if err != nil {
return nil, err
}
return img, nil
}
}
return nil, fmt.Errorf("unable to find image")
}

// SignedImageIndex implements oci.SignedImageIndex
func (i *index) SignedImageIndex(h v1.Hash) (oci.SignedImageIndex, error) {
ii, err := i.ImageIndex(h)
if err != nil {
return nil, err
}
return &index{
v1Index: ii,
}, nil
}
45 changes: 45 additions & 0 deletions pkg/oci/layout/signatures.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// Copyright 2021 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package layout

import (
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/sigstore/cosign/pkg/oci"
"github.com/sigstore/cosign/pkg/oci/internal/signature"
)

type sigs struct {
v1.Image
}

var _ oci.Signatures = (*sigs)(nil)

// Get implements oci.Signatures
func (s *sigs) Get() ([]oci.Signature, error) {
manifest, err := s.Image.Manifest()
if err != nil {
return nil, err
}
signatures := make([]oci.Signature, 0, len(manifest.Layers))
for _, desc := range manifest.Layers {
l, err := s.Image.LayerByDigest(desc.Digest)
if err != nil {
return nil, err
}
signatures = append(signatures, signature.New(l, desc))
}
return signatures, nil
}
41 changes: 15 additions & 26 deletions pkg/oci/layout/write.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
package layout

import (
"path/filepath"

v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/layout"
Expand All @@ -27,38 +25,29 @@ import (

// WriteSignedImage writes the image and all related signatures, attestations and attachments
func WriteSignedImage(path string, si oci.SignedImage) error {
// First, write the image
if err := write(path, imagePath, si); err != nil {
return errors.Wrap(err, "writing image")
// First, write an empty index
layoutPath, err := layout.Write(path, empty.Index)
if err != nil {
return err
}
// write the image
if err := appendImage(layoutPath, si, imageAnnotation); err != nil {
return errors.Wrap(err, "appending signed image")
}
// write the signatures
sigs, err := si.Signatures()
if err != nil {
return errors.Wrap(err, "getting signatures")
}
if err := write(path, signaturesPath, sigs); err != nil {
return errors.Wrap(err, "writing signatures")
if err := appendImage(layoutPath, sigs, sigsAnnotation); err != nil {
return errors.Wrap(err, "appending signatures")
}
// TODO (priyawadhwa@) write attestations and attachments
return nil
}

func imagePath(path string) string {
return filepath.Join(path, "image")
}

func signaturesPath(path string) string {
return filepath.Join(path, "sigs")
}

type pathFunc func(string) string

func write(path string, pf pathFunc, img v1.Image) error {
p := pf(path)
// write empty image
layoutPath, err := layout.Write(p, empty.Index)
if err != nil {
return err
}
// write image to disk
return layoutPath.AppendImage(img)
func appendImage(path layout.Path, img v1.Image, annotation string) error {
return path.AppendImage(img, layout.WithAnnotations(
map[string]string{annotation: "true"},
))
}
4 changes: 2 additions & 2 deletions pkg/oci/layout/write_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ func TestReadWrite(t *testing.T) {
t.Fatal(err)
}
// read the image and make sure the signatures exist
image, err := SignedImage(tmp)
imageIndex, err := SignedImageIndex(tmp)
if err != nil {
t.Fatal(err)
}
sigImage, err := image.Signatures()
sigImage, err := imageIndex.Signatures()
if err != nil {
t.Fatal(err)
}
Expand Down

0 comments on commit f8ffe71

Please sign in to comment.