Skip to content

Commit

Permalink
Migrate image.Digest to ociremote.ResolveDigest.
Browse files Browse the repository at this point in the history
The only thing left in `pkg/image` was `Digest`, which potentially hits `remote.Get`.

This change introduces `ociremote.ResolveDigest`, which is similar, but takes our options and returns a `name.Digest` instead of a `v1.Hash`.

Related: #666
Signed-off-by: Matt Moore <[email protected]>
  • Loading branch information
mattmoor committed Sep 22, 2021
1 parent a231bf9 commit 092128a
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 35 deletions.
12 changes: 4 additions & 8 deletions cmd/cosign/cli/attach/sig.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import (
ociremote "github.com/sigstore/cosign/internal/oci/remote"
"github.com/sigstore/cosign/internal/oci/static"
cremote "github.com/sigstore/cosign/pkg/cosign/remote"
"github.com/sigstore/cosign/pkg/image"
sigPayload "github.com/sigstore/sigstore/pkg/signature/payload"
)

Expand Down Expand Up @@ -71,17 +70,14 @@ func SignatureCmd(ctx context.Context, regOpts options.RegistryOpts, sigRef, pay
return err
}

remoteOpts := regOpts.GetRegistryClientOpts(ctx)

h, err := image.Digest(ref, remoteOpts...)
digest, err := ociremote.ResolveDigest(ref, regOpts.ClientOpts(ctx)...)
if err != nil {
return err
}

var payload []byte
if payloadRef == "" {
img := ref.Context().Digest(h.String())
payload, err = (&sigPayload.Cosign{Image: img}).MarshalJSON()
payload, err = (&sigPayload.Cosign{Image: digest}).MarshalJSON()
} else {
payload, err = ioutil.ReadFile(filepath.Clean(payloadRef))
}
Expand All @@ -95,7 +91,7 @@ func SignatureCmd(ctx context.Context, regOpts options.RegistryOpts, sigRef, pay
return err
}

dstRef, err := ociremote.SignatureTag(ref, ociremote.WithRemoteOptions(remoteOpts...))
dstRef, err := ociremote.SignatureTag(digest, regOpts.ClientOpts(ctx)...)
if err != nil {
return err
}
Expand All @@ -105,7 +101,7 @@ func SignatureCmd(ctx context.Context, regOpts options.RegistryOpts, sigRef, pay
return err
}

return cremote.UploadSignature(sig, dstRef, cremote.UploadOpts{RemoteOpts: remoteOpts})
return cremote.UploadSignature(sig, dstRef, cremote.UploadOpts{RemoteOpts: regOpts.GetRegistryClientOpts(ctx)})
}

type SignatureArgType uint8
Expand Down
16 changes: 7 additions & 9 deletions cmd/cosign/cli/attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"os"

"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/in-toto/in-toto-golang/in_toto"
"github.com/peterbourgon/ff/v3/ffcli"
"github.com/pkg/errors"
Expand All @@ -38,7 +39,6 @@ import (
"github.com/sigstore/cosign/pkg/cosign"
"github.com/sigstore/cosign/pkg/cosign/attestation"
cremote "github.com/sigstore/cosign/pkg/cosign/remote"
"github.com/sigstore/cosign/pkg/image"
"github.com/sigstore/cosign/pkg/signature"
"github.com/sigstore/cosign/pkg/types"
rekorClient "github.com/sigstore/rekor/pkg/client"
Expand Down Expand Up @@ -144,16 +144,15 @@ func AttestCmd(ctx context.Context, ko sign.KeyOpts, regOpts options.RegistryOpt
return fmt.Errorf("invalid predicate type: %s", predicateType)
}

remoteOpts := regOpts.GetRegistryClientOpts(ctx)

ref, err := name.ParseReference(imageRef)
if err != nil {
return errors.Wrap(err, "parsing reference")
}
h, err := image.Digest(ref, remoteOpts...)
digest, err := ociremote.ResolveDigest(ref, regOpts.ClientOpts(ctx)...)
if err != nil {
return err
}
h, _ := v1.NewHash(digest.Identifier())

sv, err := sign.SignerFromKeyOpts(ctx, certPath, ko)
if err != nil {
Expand All @@ -162,12 +161,11 @@ func AttestCmd(ctx context.Context, ko sign.KeyOpts, regOpts options.RegistryOpt
wrapped := dsse.WrapSigner(sv, predicateURI)

fmt.Fprintln(os.Stderr, "Using payload from:", predicatePath)

sh, err := attestation.GenerateStatement(attestation.GenerateOpts{
Path: predicatePath,
Type: predicateType,
Digest: h.Hex,
Repo: ref.Context().String(),
Repo: digest.Repository.String(),
})
if err != nil {
return err
Expand All @@ -192,7 +190,7 @@ func AttestCmd(ctx context.Context, ko sign.KeyOpts, regOpts options.RegistryOpt
opts = append(opts, static.WithCertChain(sv.Cert, sv.Chain))
}

uploadTLog, err := sign.ShouldUploadToTlog(ref, force, ko.RekorURL)
uploadTLog, err := sign.ShouldUploadToTlog(digest, force, ko.RekorURL)
if err != nil {
return err
}
Expand Down Expand Up @@ -223,7 +221,7 @@ func AttestCmd(ctx context.Context, ko sign.KeyOpts, regOpts options.RegistryOpt
opts = append(opts, static.WithBundle(sign.Bundle(entry)))
}

attRef, err := ociremote.AttestationTag(ref, ociremote.WithRemoteOptions(remoteOpts...))
attRef, err := ociremote.AttestationTag(digest, regOpts.ClientOpts(ctx)...)
if err != nil {
return err
}
Expand All @@ -238,6 +236,6 @@ func AttestCmd(ctx context.Context, ko sign.KeyOpts, regOpts options.RegistryOpt
// in the payload field since they can get large
return cremote.UploadSignature(sig, attRef, cremote.UploadOpts{
DupeDetector: cremote.NewDupeDetector(sv),
RemoteOpts: remoteOpts,
RemoteOpts: regOpts.GetRegistryClientOpts(ctx),
})
}
7 changes: 3 additions & 4 deletions cmd/cosign/cli/generate/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
"github.com/peterbourgon/ff/v3/ffcli"

"github.com/sigstore/cosign/cmd/cosign/cli/options"
"github.com/sigstore/cosign/pkg/image"
ociremote "github.com/sigstore/cosign/internal/oci/remote"
"github.com/sigstore/cosign/pkg/signature"
"github.com/sigstore/sigstore/pkg/signature/payload"
)
Expand Down Expand Up @@ -74,13 +74,12 @@ func GenerateCmd(ctx context.Context, regOpts options.RegistryOpts, imageRef str
return err
}

h, err := image.Digest(ref, regOpts.GetRegistryClientOpts(ctx)...)
digest, err := ociremote.ResolveDigest(ref, regOpts.ClientOpts(ctx)...)
if err != nil {
return err
}
img := ref.Context().Digest(h.String())

json, err := (&payload.Cosign{Image: img, Annotations: annotations}).MarshalJSON()
json, err := (&payload.Cosign{Image: digest, Annotations: annotations}).MarshalJSON()
if err != nil {
return err
}
Expand Down
9 changes: 4 additions & 5 deletions cmd/cosign/cli/sign/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ import (
"github.com/sigstore/cosign/pkg/cosign"
"github.com/sigstore/cosign/pkg/cosign/pivkey"
cremote "github.com/sigstore/cosign/pkg/cosign/remote"
"github.com/sigstore/cosign/pkg/image"
providers "github.com/sigstore/cosign/pkg/providers/all"
sigs "github.com/sigstore/cosign/pkg/signature"
fulcioClient "github.com/sigstore/fulcio/pkg/client"
Expand Down Expand Up @@ -248,19 +247,19 @@ func SignCmd(ctx context.Context, ko KeyOpts, regOpts options.RegistryOpts, anno
return fmt.Errorf("unable to resolve attachment %s for image %s", attachment, inputImg)
}

h, err := image.Digest(ref, remoteOpts...)
digest, err := ociremote.ResolveDigest(ref, regOpts.ClientOpts(ctx)...)
if err != nil {
return errors.Wrap(err, "resolving digest")
}
toSign = append(toSign, ref.Context().Digest(h.String()))
toSign = append(toSign, digest)

if recursive {
get, err := remote.Get(ref, remoteOpts...)
get, err := remote.Get(digest, remoteOpts...)
if err != nil {
return errors.Wrap(err, "getting remote image")
}
if get.MediaType.IsIndex() {
imgs, err := getTransitiveImages(get, ref.Context(), remoteOpts...)
imgs, err := getTransitiveImages(get, digest.Repository, remoteOpts...)
if err != nil {
return err
}
Expand Down
20 changes: 11 additions & 9 deletions pkg/image/image.go → internal/oci/remote/digest.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,27 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package image
package remote

import (
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/remote"
)

// Digest returns the digest of the image at the reference.
// ResolveDigest returns the digest of the image at the reference.
//
// If the reference is by digest already, it simply extracts the digest.
// Otherwise, it looks up the digest from the registry.
func Digest(ref name.Reference, remoteOpts ...remote.Option) (v1.Hash, error) {
func ResolveDigest(ref name.Reference, opts ...Option) (name.Digest, error) {
o, err := makeOptions(ref.Context(), opts...)
if err != nil {
return name.Digest{}, err
}
if d, ok := ref.(name.Digest); ok {
return v1.NewHash(d.DigestStr())
return d, nil
}
desc, err := remote.Get(ref, remoteOpts...)
desc, err := remoteGet(ref, o.ROpt...)
if err != nil {
return v1.Hash{}, err
return name.Digest{}, err
}
return desc.Digest, nil
return ref.Context().Digest(desc.Digest.String()), nil
}
86 changes: 86 additions & 0 deletions internal/oci/remote/digest_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// 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 remote

import (
"testing"

"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/pkg/errors"
)

func TestResolveDigest(t *testing.T) {
rg := remoteGet
defer func() {
remoteGet = rg
}()

tag := name.MustParseReference("gcr.io/distroless/static:nonroot")
// As of 2021-09-20:
// crane digest gcr.io/distroless/static:nonroot
digest := name.MustParseReference("gcr.io/distroless/static@sha256:be5d77c62dbe7fedfb0a4e5ec2f91078080800ab1f18358e5f31fcc8faa023c4")

t.Run("digest doesn't call remote.Get", func(t *testing.T) {
remoteGet = func(ref name.Reference, options ...remote.Option) (*remote.Descriptor, error) {
t.Fatal("ResolveDigest should not call remote.Get.")
return nil, nil
}

got, err := ResolveDigest(digest)
if err != nil {
t.Fatalf("ResolveDigest() = %v", err)
}
if want := digest; got != want {
t.Errorf("ResolveDigest() = %v, wanted %v", got, want)
}
})

t.Run("tag calls remote.Get", func(t *testing.T) {
remoteGet = func(ref name.Reference, options ...remote.Option) (*remote.Descriptor, error) {
return &remote.Descriptor{
Descriptor: v1.Descriptor{
Digest: v1.Hash{
Algorithm: "sha256",
// As of 2021-09-20:
// crane digest gcr.io/distroless/static:nonroot
Hex: "be5d77c62dbe7fedfb0a4e5ec2f91078080800ab1f18358e5f31fcc8faa023c4",
},
},
}, nil
}

got, err := ResolveDigest(tag)
if err != nil {
t.Fatalf("ResolveDigest() = %v", err)
}
if want := digest; got != want {
t.Errorf("ResolveDigest() = %v, wanted %v", got, want)
}
})

t.Run("remote.Get errors propagate", func(t *testing.T) {
want := errors.New("we should propagate this error")
remoteGet = func(ref name.Reference, options ...remote.Option) (*remote.Descriptor, error) {
return nil, want
}

_, got := ResolveDigest(tag)
if !errors.Is(got, want) {
t.Fatalf("ResolveDigest() = %v, wanted %v", got, want)
}
})
}

0 comments on commit 092128a

Please sign in to comment.