Support referrers using digest, not just tag (#55)

* Support referrers using digest, not just tag

* ParseRef and switch on type

* Call DigestStr instead of String
This commit is contained in:
James Carnegie
2024-06-17 17:30:12 +01:00
committed by GitHub
parent 0d0d86854c
commit 130e1f640b
2 changed files with 72 additions and 31 deletions

View File

@@ -198,6 +198,7 @@ func (r *OCILayoutResolver) ImageDigest(ctx context.Context) (string, error) {
type ReferrersResolver struct {
image string
platform *v1.Platform
digest string
referrersRepo string
manifests []*AttestationManifest
}
@@ -236,27 +237,20 @@ func WithPlatform(platform string) func(*ReferrersResolver) error {
func (r *ReferrersResolver) resolveAttestations(ctx context.Context) error {
if r.manifests == nil {
options := withOptions(ctx, r.platform)
subjectRef, err := name.ParseReference(r.image)
if err != nil {
return fmt.Errorf("failed to parse reference: %w", err)
}
desc, err := remote.Image(subjectRef, options...)
if err != nil {
return fmt.Errorf("failed to get image manifest: %w", err)
}
subjectDigest, err := desc.Digest()
if err != nil {
return fmt.Errorf("failed to get image digest: %w", err)
}
subjectDigest, err := r.ImageDigest(ctx)
var referrersSubjectRef name.Digest
if r.referrersRepo != "" {
referrersSubjectRef, err = name.NewDigest(fmt.Sprintf("%s@%s", r.referrersRepo, subjectDigest.String()))
referrersSubjectRef, err = name.NewDigest(fmt.Sprintf("%s@%s", r.referrersRepo, subjectDigest))
if err != nil {
return fmt.Errorf("failed to create referrers reference: %w", err)
}
} else {
referrersSubjectRef = subjectRef.Context().Digest(subjectDigest.String())
referrersSubjectRef = subjectRef.Context().Digest(subjectDigest)
}
referrersIndex, err := remote.Referrers(referrersSubjectRef)
if err != nil {
@@ -284,7 +278,7 @@ func (r *ReferrersResolver) resolveAttestations(ctx context.Context) error {
if manifest.Annotations[att.DockerReferenceType] != AttestationManifestType {
continue
}
if manifest.Annotations[att.DockerReferenceDigest] != subjectDigest.String() {
if manifest.Annotations[att.DockerReferenceDigest] != subjectDigest {
continue
}
attest := &AttestationManifest{
@@ -292,7 +286,7 @@ func (r *ReferrersResolver) resolveAttestations(ctx context.Context) error {
Image: attestationImage,
Manifest: manifest,
Descriptor: &m,
Digest: subjectDigest.String(),
Digest: subjectDigest,
Platform: r.platform,
}
aManifests = append(aManifests, attest)
@@ -331,14 +325,30 @@ func (r *ReferrersResolver) ImagePlatform() (*v1.Platform, error) {
}
func (r *ReferrersResolver) ImageDigest(ctx context.Context) (string, error) {
err := r.resolveAttestations(ctx)
if err != nil {
return "", fmt.Errorf("failed to resolve attestations: %w", err)
if r.digest == "" {
subjectRef, err := name.ParseReference(r.image)
if err != nil {
return "", fmt.Errorf("failed to parse reference: %w", err)
}
switch t := subjectRef.(type) {
case name.Digest:
r.digest = t.DigestStr()
case name.Tag:
options := WithOptions(ctx, r.platform)
desc, err := remote.Image(t, options...)
if err != nil {
return "", fmt.Errorf("failed to get image manifest: %w", err)
}
subjectDigest, err := desc.Digest()
if err != nil {
return "", fmt.Errorf("failed to get image digest: %w", err)
}
r.digest = subjectDigest.String()
default:
return "", fmt.Errorf("unsupported reference type: %T", t)
}
}
if len(r.manifests) == 0 {
return "", errors.New("no attestation manifests found")
}
return r.manifests[0].Digest, nil
return r.digest, nil
}
type RegistryResolver struct {
@@ -390,20 +400,20 @@ func (r *RegistryResolver) Attestations(ctx context.Context, predicateType strin
func FetchAttestationManifest(ctx context.Context, image string, platform *v1.Platform) (*AttestationManifest, error) {
// we want to get to the image index, so ignoring platform for now
options := withOptions(ctx, nil)
options := WithOptions(ctx, nil)
ref, err := name.ParseReference(image)
if err != nil {
return nil, fmt.Errorf("failed to parse reference: %w", err)
}
desc, err := remote.Index(ref, options...)
index, err := remote.Index(ref, options...)
if err != nil {
return nil, fmt.Errorf("failed to obtain index manifest: %w", err)
return nil, fmt.Errorf("failed to get index: %w", err)
}
ix, err := desc.IndexManifest()
indexManifest, err := index.IndexManifest()
if err != nil {
return nil, fmt.Errorf("failed to obtain index manifest: %w", err)
return nil, fmt.Errorf("failed to get index manifest: %w", err)
}
digest, err := imageDigestForPlatform(ix, platform)
digest, err := imageDigestForPlatform(indexManifest, platform)
if err != nil {
return nil, fmt.Errorf("failed to obtain image for platform: %w", err)
}
@@ -412,7 +422,7 @@ func FetchAttestationManifest(ctx context.Context, image string, platform *v1.Pl
return nil, fmt.Errorf("failed to parse attestation reference: %w", err)
}
attestationDigest, err := attestationDigestForDigest(ix, digest, "attestation-manifest")
attestationDigest, err := attestationDigestForDigest(indexManifest, digest, "attestation-manifest")
if err != nil {
return nil, fmt.Errorf("failed to obtain attestation for image: %w", err)
}
@@ -444,7 +454,7 @@ func FetchAttestationManifest(ctx context.Context, image string, platform *v1.Pl
return attest, nil
}
func withOptions(ctx context.Context, platform *v1.Platform) []remote.Option {
func WithOptions(ctx context.Context, platform *v1.Platform) []remote.Option {
// prepare options
options := []remote.Option{remote.WithAuthFromKeychain(authn.DefaultKeychain), remote.WithTransport(HttpTransport()), remote.WithContext(ctx)}