...

Source file src/github.com/google/go-containerregistry/pkg/v1/remote/referrers.go

Documentation: github.com/google/go-containerregistry/pkg/v1/remote

     1  // Copyright 2023 Google LLC All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package remote
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"errors"
    21  	"io"
    22  	"net/http"
    23  	"strings"
    24  
    25  	"github.com/google/go-containerregistry/pkg/name"
    26  	v1 "github.com/google/go-containerregistry/pkg/v1"
    27  	"github.com/google/go-containerregistry/pkg/v1/empty"
    28  	"github.com/google/go-containerregistry/pkg/v1/mutate"
    29  	"github.com/google/go-containerregistry/pkg/v1/remote/transport"
    30  	"github.com/google/go-containerregistry/pkg/v1/types"
    31  )
    32  
    33  // Referrers returns a list of descriptors that refer to the given manifest digest.
    34  //
    35  // The subject manifest doesn't have to exist in the registry for there to be descriptors that refer to it.
    36  func Referrers(d name.Digest, options ...Option) (v1.ImageIndex, error) {
    37  	o, err := makeOptions(options...)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  	return newPuller(o).referrers(o.context, d, o.filter)
    42  }
    43  
    44  // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#referrers-tag-schema
    45  func fallbackTag(d name.Digest) name.Tag {
    46  	return d.Context().Tag(strings.Replace(d.DigestStr(), ":", "-", 1))
    47  }
    48  
    49  func (f *fetcher) fetchReferrers(ctx context.Context, filter map[string]string, d name.Digest) (v1.ImageIndex, error) {
    50  	// Check the Referrers API endpoint first.
    51  	u := f.url("referrers", d.DigestStr())
    52  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	req.Header.Set("Accept", string(types.OCIImageIndex))
    57  
    58  	resp, err := f.client.Do(req)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	defer resp.Body.Close()
    63  
    64  	if err := transport.CheckError(resp, http.StatusOK, http.StatusNotFound, http.StatusBadRequest); err != nil {
    65  		return nil, err
    66  	}
    67  
    68  	var b []byte
    69  	if resp.StatusCode == http.StatusOK {
    70  		b, err = io.ReadAll(resp.Body)
    71  		if err != nil {
    72  			return nil, err
    73  		}
    74  	} else {
    75  		// The registry doesn't support the Referrers API endpoint, so we'll use the fallback tag scheme.
    76  		b, _, err = f.fetchManifest(ctx, fallbackTag(d), []types.MediaType{types.OCIImageIndex})
    77  		var terr *transport.Error
    78  		if errors.As(err, &terr) && terr.StatusCode == http.StatusNotFound {
    79  			// Not found just means there are no attachments yet. Start with an empty manifest.
    80  			return empty.Index, nil
    81  		} else if err != nil {
    82  			return nil, err
    83  		}
    84  	}
    85  
    86  	h, sz, err := v1.SHA256(bytes.NewReader(b))
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	idx := &remoteIndex{
    91  		fetcher:   *f,
    92  		ctx:       ctx,
    93  		manifest:  b,
    94  		mediaType: types.OCIImageIndex,
    95  		descriptor: &v1.Descriptor{
    96  			Digest:    h,
    97  			MediaType: types.OCIImageIndex,
    98  			Size:      sz,
    99  		},
   100  	}
   101  	return filterReferrersResponse(filter, idx), nil
   102  }
   103  
   104  // If filter applied, filter out by artifactType.
   105  // See https://github.com/opencontainers/distribution-spec/blob/main/spec.md#listing-referrers
   106  func filterReferrersResponse(filter map[string]string, in v1.ImageIndex) v1.ImageIndex {
   107  	if filter == nil {
   108  		return in
   109  	}
   110  	v, ok := filter["artifactType"]
   111  	if !ok {
   112  		return in
   113  	}
   114  	return mutate.RemoveManifests(in, func(desc v1.Descriptor) bool {
   115  		return desc.ArtifactType != v
   116  	})
   117  }
   118  

View as plain text