Skip to content

Commit

Permalink
add fork changes to 2.4.x branch
Browse files Browse the repository at this point in the history
Signed-off-by: Adam Martin <[email protected]>
  • Loading branch information
amartin120 committed Jan 8, 2025
2 parents 9a4cfe1 + d69820b commit b8416a2
Show file tree
Hide file tree
Showing 14 changed files with 365 additions and 43 deletions.
38 changes: 28 additions & 10 deletions cmd/cosign/cli/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,19 @@ func Load() *cobra.Command {
o := &options.LoadOptions{}

cmd := &cobra.Command{
Use: "load",
Short: "Load a signed image on disk to a remote registry",
Long: "Load a signed image on disk to a remote registry",
Example: ` cosign load --dir <path to directory> <IMAGE>`,
Args: cobra.ExactArgs(1),
Use: "load",
Short: "Load a signed image on disk to a remote registry",
Long: "Load a signed image on disk to a remote registry",
Example: ` cosign load --dir <path to directory> <IMAGE> OR cosign load --dir <path to directory> --registry <REGISTRY>`,
//Args: cobra.ExactArgs(1),
PersistentPreRun: options.BindViper,
RunE: func(cmd *cobra.Command, args []string) error {
if o.Registry.Name != "" && len(args) == 0 {
return LoadCmd(cmd.Context(), *o, "")
}
if len(args) != 1 {
return fmt.Errorf("image argument is required")
}
return LoadCmd(cmd.Context(), *o, args[0])
},
}
Expand All @@ -48,12 +54,21 @@ func Load() *cobra.Command {
}

func LoadCmd(ctx context.Context, opts options.LoadOptions, imageRef string) error {
ref, err := name.ParseReference(imageRef)
if err != nil {
return fmt.Errorf("parsing image name %s: %w", imageRef, err)
if opts.Registry.Name != "" && imageRef != "" {
return fmt.Errorf("both --registry and image argument provided, only one should be used")
}

var ref name.Reference
var err error
if opts.Registry.Name == "" {
// Use the provided image reference
ref, err = name.ParseReference(imageRef)
if err != nil {
return fmt.Errorf("parsing image name %s: %w", imageRef, err)
}
}

// get the signed image from disk
// get the signed image(s) from disk
sii, err := layout.SignedImageIndex(opts.Directory)
if err != nil {
return fmt.Errorf("signed image index: %w", err)
Expand All @@ -64,5 +79,8 @@ func LoadCmd(ctx context.Context, opts options.LoadOptions, imageRef string) err
return err
}

return remote.WriteSignedImageIndexImages(ref, sii, ociremoteOpts...)
if opts.Registry.Name == "" {
return remote.WriteSignedImageIndexImages(ref, sii, ociremoteOpts...)
}
return remote.WriteSignedImageIndexImagesBulk(opts.Registry.Name, sii, ociremoteOpts...)
}
4 changes: 4 additions & 0 deletions cmd/cosign/cli/options/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,8 @@ func (o *LoadOptions) AddFlags(cmd *cobra.Command) {
"path to directory where the signed image is stored on disk")
_ = cmd.Flags().SetAnnotation("dir", cobra.BashCompSubdirsInDir, []string{})
_ = cmd.MarkFlagRequired("dir")

cmd.Flags().StringVar(&o.Registry.Name, "registry", "",
"registry to use for bulk load")
_ = cmd.Flags().SetAnnotation("registry", cobra.BashCompSubdirsInDir, []string{})
}
1 change: 1 addition & 0 deletions cmd/cosign/cli/options/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type RegistryOptions struct {
AllowInsecure bool
AllowHTTPRegistry bool
KubernetesKeychain bool
Name string
RefOpts ReferenceOptions
Keychain Keychain
AuthConfig authn.AuthConfig
Expand Down
8 changes: 8 additions & 0 deletions cmd/cosign/cli/options/save.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (
type SaveOptions struct {
Directory string
Registry RegistryOptions
Platform string
CachePath string
}

var _ Interface = (*SaveOptions)(nil)
Expand All @@ -34,4 +36,10 @@ func (o *SaveOptions) AddFlags(cmd *cobra.Command) {
"path to dir where the signed image should be stored on disk")
_ = cmd.Flags().SetAnnotation("dir", cobra.BashCompSubdirsInDir, []string{})
_ = cmd.MarkFlagRequired("dir")

cmd.Flags().StringVar(&o.Platform, "platform", "",
"only save container image and its signatures for a specific platform image")

cmd.Flags().StringVarP(&o.CachePath, "cache-path", "c", "",
"path to cache image layers")
}
12 changes: 10 additions & 2 deletions cmd/cosign/cli/save.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
"github.com/sigstore/cosign/v2/pkg/oci"
"github.com/sigstore/cosign/v2/pkg/oci/layout"
ociplatform "github.com/sigstore/cosign/v2/pkg/oci/platform"
ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote"
"github.com/spf13/cobra"
)
Expand All @@ -50,6 +51,7 @@ func Save() *cobra.Command {
func SaveCmd(ctx context.Context, opts options.SaveOptions, imageRef string) error {
regOpts := opts.Registry
regClientOpts, err := regOpts.ClientOpts(ctx)
regClientOpts = append(regClientOpts, ociremote.WithCachePath(opts.CachePath))
if err != nil {
return fmt.Errorf("constructing client options: %w", err)
}
Expand All @@ -64,20 +66,26 @@ func SaveCmd(ctx context.Context, opts options.SaveOptions, imageRef string) err
return fmt.Errorf("signed entity: %w", err)
}

se, err = ociplatform.SignedEntityForPlatform(se, opts.Platform)
if err != nil {
return err
}

if _, ok := se.(oci.SignedImage); ok {
si, err := ociremote.SignedImage(ref, regClientOpts...)
if err != nil {
return fmt.Errorf("getting signed image: %w", err)
}
return layout.WriteSignedImage(opts.Directory, si)
return layout.WriteSignedImage(opts.Directory, si, ref)
}

if _, ok := se.(oci.SignedImageIndex); ok {
sii, err := ociremote.SignedImageIndex(ref, regClientOpts...)
if err != nil {
return fmt.Errorf("getting signed image index: %w", err)
}
return layout.WriteSignedImageIndex(opts.Directory, sii)
return layout.WriteSignedImageIndex(opts.Directory, sii, ref)
}

return errors.New("unknown signed entity")
}
3 changes: 2 additions & 1 deletion doc/cosign_load.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions doc/cosign_save.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 14 additions & 11 deletions pkg/oci/layout/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@ import (
)

const (
kindAnnotation = "kind"
imageAnnotation = "dev.cosignproject.cosign/image"
imageIndexAnnotation = "dev.cosignproject.cosign/imageIndex"
sigsAnnotation = "dev.cosignproject.cosign/sigs"
attsAnnotation = "dev.cosignproject.cosign/atts"
KindAnnotation = "kind"
ImageAnnotation = "dev.cosignproject.cosign/image"
ImageRefAnnotation = "org.opencontainers.image.ref.name"
ContainerdNameAnnotation = "io.containerd.image.name"
ImageIndexAnnotation = "dev.cosignproject.cosign/imageIndex"
SigsAnnotation = "dev.cosignproject.cosign/sigs"
AttsAnnotation = "dev.cosignproject.cosign/atts"
SbomsAnnotation = "dev.cosignproject.cosign/sboms"
)

// SignedImageIndex provides access to a local index reference, and its signatures.
Expand Down Expand Up @@ -59,7 +62,7 @@ var _ oci.SignedImageIndex = (*index)(nil)

// Signatures implements oci.SignedImageIndex
func (i *index) Signatures() (oci.Signatures, error) {
img, err := i.imageByAnnotation(sigsAnnotation)
img, err := i.imageByAnnotation(SigsAnnotation)
if err != nil {
return nil, err
}
Expand All @@ -71,7 +74,7 @@ func (i *index) Signatures() (oci.Signatures, error) {

// Attestations implements oci.SignedImageIndex
func (i *index) Attestations() (oci.Signatures, error) {
img, err := i.imageByAnnotation(attsAnnotation)
img, err := i.imageByAnnotation(AttsAnnotation)
if err != nil {
return nil, err
}
Expand All @@ -92,7 +95,7 @@ func (i *index) SignedImage(h v1.Hash) (oci.SignedImage, error) {
var img v1.Image
var err error
if h.String() == ":" {
img, err = i.imageByAnnotation(imageAnnotation)
img, err = i.imageByAnnotation(ImageAnnotation)
} else {
img, err = i.Image(h)
}
Expand All @@ -113,7 +116,7 @@ func (i *index) imageByAnnotation(annotation string) (v1.Image, error) {
return nil, err
}
for _, m := range manifest.Manifests {
if val, ok := m.Annotations[kindAnnotation]; ok && val == annotation {
if val, ok := m.Annotations[KindAnnotation]; ok && val == annotation {
return i.Image(m.Digest)
}
}
Expand All @@ -126,7 +129,7 @@ func (i *index) imageIndexByAnnotation(annotation string) (v1.ImageIndex, error)
return nil, err
}
for _, m := range manifest.Manifests {
if val, ok := m.Annotations[kindAnnotation]; ok && val == annotation {
if val, ok := m.Annotations[KindAnnotation]; ok && val == annotation {
return i.ImageIndex(m.Digest)
}
}
Expand All @@ -138,7 +141,7 @@ func (i *index) SignedImageIndex(h v1.Hash) (oci.SignedImageIndex, error) {
var ii v1.ImageIndex
var err error
if h.String() == ":" {
ii, err = i.imageIndexByAnnotation(imageIndexAnnotation)
ii, err = i.imageIndexByAnnotation(ImageIndexAnnotation)
} else {
ii, err = i.ImageIndex(h)
}
Expand Down
72 changes: 56 additions & 16 deletions pkg/oci/layout/write.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,52 +16,69 @@
package layout

import (
"errors"
"fmt"
"os"
"strings"

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

// WriteSignedImage writes the image and all related signatures, attestations and attachments
func WriteSignedImage(path string, si oci.SignedImage) error {
// First, write an empty index
layoutPath, err := layout.Write(path, empty.Index)
func WriteSignedImage(path string, si oci.SignedImage, ref name.Reference) error {
layoutPath, err := layout.FromPath(path)
if os.IsNotExist(err) {
// If the layout doesn't exist, create a new one
layoutPath, err = layout.Write(path, empty.Index)
}
if err != nil {
return err
}

// write the image
if err := appendImage(layoutPath, si, imageAnnotation); err != nil {
if err := appendImage(layoutPath, si, ref, ImageAnnotation); err != nil {
return fmt.Errorf("appending signed image: %w", err)
}
return writeSignedEntity(layoutPath, si)
return writeSignedEntity(layoutPath, si, ref)
}

// WriteSignedImageIndex writes the image index and all related signatures, attestations and attachments
func WriteSignedImageIndex(path string, si oci.SignedImageIndex) error {
// First, write an empty index
layoutPath, err := layout.Write(path, empty.Index)
func WriteSignedImageIndex(path string, si oci.SignedImageIndex, ref name.Reference) error {
layoutPath, err := layout.FromPath(path)
if os.IsNotExist(err) {
// If the layout doesn't exist, create a new one
layoutPath, err = layout.Write(path, empty.Index)
}
if err != nil {
return err
}
// write the image index

// Append the image index
imageRef, err := getImageRef(ref)
if err != nil {
return err // Return the error from getImageRef immediately.
}
if err := layoutPath.AppendIndex(si, layout.WithAnnotations(
map[string]string{kindAnnotation: imageIndexAnnotation},
map[string]string{KindAnnotation: ImageIndexAnnotation, ImageRefAnnotation: imageRef, ContainerdNameAnnotation: ref.Name()},
)); err != nil {
return fmt.Errorf("appending signed image index: %w", err)
}
return writeSignedEntity(layoutPath, si)

return writeSignedEntity(layoutPath, si, ref)
}

func writeSignedEntity(path layout.Path, se oci.SignedEntity) error {
func writeSignedEntity(path layout.Path, se oci.SignedEntity, ref name.Reference) error {
// write the signatures
sigs, err := se.Signatures()
if err != nil {
return fmt.Errorf("getting signatures: %w", err)
}
if !isEmpty(sigs) {
if err := appendImage(path, sigs, sigsAnnotation); err != nil {
if err := appendImage(path, sigs, ref, SigsAnnotation); err != nil {
return fmt.Errorf("appending signatures: %w", err)
}
}
Expand All @@ -72,11 +89,20 @@ func writeSignedEntity(path layout.Path, se oci.SignedEntity) error {
return fmt.Errorf("getting atts")
}
if !isEmpty(atts) {
if err := appendImage(path, atts, attsAnnotation); err != nil {
if err := appendImage(path, atts, ref, AttsAnnotation); err != nil {
return fmt.Errorf("appending atts: %w", err)
}
}

// TODO (priyawadhwa@) and attachments
// temp handle sboms - amartin120@
sboms, err := se.Attachment("sbom")
if err != nil {
return nil // no sbom found
}
if err := appendImage(path, sboms, ref, SbomsAnnotation); err != nil {
return fmt.Errorf("appending attachments: %w", err)
}
return nil
}

Expand All @@ -86,8 +112,22 @@ func isEmpty(s oci.Signatures) bool {
return ss == nil
}

func appendImage(path layout.Path, img v1.Image, annotation string) error {
func appendImage(path layout.Path, img v1.Image, ref name.Reference, annotation string) error {
imageRef, err := getImageRef(ref)
if err != nil {
return err // Return the error from getImageRef immediately.
}
return path.AppendImage(img, layout.WithAnnotations(
map[string]string{kindAnnotation: annotation},
map[string]string{KindAnnotation: annotation, ImageRefAnnotation: imageRef, ContainerdNameAnnotation: ref.Name()},
))
}

func getImageRef(ref name.Reference) (string, error) {
if ref == nil {
return "", errors.New("reference is nil")
}
registry := ref.Context().RegistryStr() + "/"
imageRef := ref.Name()
imageRef = strings.TrimPrefix(imageRef, registry)
return imageRef, nil
}
Loading

0 comments on commit b8416a2

Please sign in to comment.