oras.land/oras-go/v2@v2.5.1-0.20240520045656-aef90e4d04c4/registry/remote/example_test.go (about)

     1  /*
     2  Copyright The ORAS Authors.
     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  http://www.apache.org/licenses/LICENSE-2.0
     7  Unless required by applicable law or agreed to in writing, software
     8  distributed under the License is distributed on an "AS IS" BASIS,
     9  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    10  See the License for the specific language governing permissions and
    11  limitations under the License.
    12  */
    13  // Package remote_test includes all the testable examples for remote repository type
    14  
    15  package remote_test
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"encoding/json"
    21  	"errors"
    22  	"fmt"
    23  	"io"
    24  	"net/http"
    25  	"net/http/httptest"
    26  	"net/url"
    27  	"os"
    28  	"strconv"
    29  	"strings"
    30  	"testing"
    31  
    32  	"github.com/opencontainers/go-digest"
    33  	"github.com/opencontainers/image-spec/specs-go"
    34  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    35  	"oras.land/oras-go/v2"
    36  	"oras.land/oras-go/v2/content"
    37  	"oras.land/oras-go/v2/internal/spec"
    38  	. "oras.land/oras-go/v2/registry/internal/doc"
    39  	"oras.land/oras-go/v2/registry/remote"
    40  )
    41  
    42  const (
    43  	_                     = ExampleUnplayable
    44  	exampleRepositoryName = "example"
    45  	exampleTag            = "latest"
    46  	exampleConfig         = "Example config content"
    47  	exampleLayer          = "Example layer content"
    48  	exampleUploadUUid     = "0bc84d80-837c-41d9-824e-1907463c53b3"
    49  	// For ExampleRepository_Push_artifactReferenceManifest:
    50  	ManifestDigest          = "sha256:a3f9d449466b9b7194c3a76ca4890d792e11eb4e62e59aa8b4c3cce0a56f129d"
    51  	ReferenceManifestDigest = "sha256:2d30397701742b04550891851529abe6b071e4fae920a91897d34612662a3bf6"
    52  	// For Example_pushAndIgnoreReferrersIndexError:
    53  	referrersAPIUnavailableRepositoryName = "no-referrers-api"
    54  	referrerDigest                        = "sha256:4caba1e18385eb152bd92e9fee1dc01e47c436e594123b3c2833acfcad9883e2"
    55  	referrersTag                          = "sha256-c824a9aa7d2e3471306648c6d4baa1abbcb97ff0276181ab4722ca27127cdba0"
    56  	referrerIndexDigest                   = "sha256:7baac5147dd58d56fdbaad5a888fa919235a3a90cb71aaa8b56ee5d19f4cd838"
    57  )
    58  
    59  var (
    60  	exampleLayerDescriptor = content.NewDescriptorFromBytes(ocispec.MediaTypeImageLayer, []byte(exampleLayer))
    61  	exampleLayerDigest     = exampleLayerDescriptor.Digest.String()
    62  	exampleManifest, _     = json.Marshal(ocispec.Manifest{
    63  		Versioned: specs.Versioned{
    64  			SchemaVersion: 2, // historical value. does not pertain to OCI or docker version
    65  		},
    66  		Config: content.NewDescriptorFromBytes(ocispec.MediaTypeImageConfig, []byte(exampleConfig)),
    67  		Layers: []ocispec.Descriptor{
    68  			exampleLayerDescriptor,
    69  		},
    70  	})
    71  	exampleManifestDescriptor   = content.NewDescriptorFromBytes(ocispec.MediaTypeImageManifest, exampleManifest)
    72  	exampleManifestDigest       = exampleManifestDescriptor.Digest.String()
    73  	exampleSignatureManifest, _ = json.Marshal(spec.Artifact{
    74  		MediaType:    spec.MediaTypeArtifactManifest,
    75  		ArtifactType: "example/signature",
    76  		Subject:      &exampleManifestDescriptor})
    77  	exampleSignatureManifestDescriptor = ocispec.Descriptor{
    78  		MediaType:    spec.MediaTypeArtifactManifest,
    79  		ArtifactType: "example/signature",
    80  		Digest:       digest.FromBytes(exampleSignatureManifest),
    81  		Size:         int64(len(exampleSignatureManifest))}
    82  	exampleSBoMManifest, _ = json.Marshal(spec.Artifact{
    83  		MediaType:    spec.MediaTypeArtifactManifest,
    84  		ArtifactType: "example/SBoM",
    85  		Subject:      &exampleManifestDescriptor})
    86  	exampleSBoMManifestDescriptor = ocispec.Descriptor{
    87  		MediaType:    spec.MediaTypeArtifactManifest,
    88  		ArtifactType: "example/SBoM",
    89  		Digest:       digest.FromBytes(exampleSBoMManifest),
    90  		Size:         int64(len(exampleSBoMManifest))}
    91  	exampleReferrerDescriptors = [][]ocispec.Descriptor{
    92  		{exampleSBoMManifestDescriptor},      // page 0
    93  		{exampleSignatureManifestDescriptor}, // page 1
    94  	}
    95  	blobContent    = "example blob content"
    96  	blobDescriptor = ocispec.Descriptor{
    97  		MediaType: "application/tar",
    98  		Digest:    digest.FromBytes([]byte(blobContent)),
    99  		Size:      int64(len(blobContent))}
   100  	exampleManifestWithBlobs, _ = json.Marshal(spec.Artifact{
   101  		MediaType:    spec.MediaTypeArtifactManifest,
   102  		ArtifactType: "example/manifest",
   103  		Blobs:        []ocispec.Descriptor{blobDescriptor},
   104  		Subject:      &exampleManifestDescriptor})
   105  	exampleManifestWithBlobsDescriptor = ocispec.Descriptor{
   106  		MediaType:    spec.MediaTypeArtifactManifest,
   107  		ArtifactType: "example/manifest",
   108  		Digest:       digest.FromBytes(exampleManifestWithBlobs),
   109  		Size:         int64(len(exampleManifestWithBlobs))}
   110  	subjectDescriptor          = content.NewDescriptorFromBytes(ocispec.MediaTypeImageManifest, []byte(`{"layers":[]}`))
   111  	referrerManifestContent, _ = json.Marshal(ocispec.Manifest{
   112  		Versioned: specs.Versioned{SchemaVersion: 2},
   113  		MediaType: ocispec.MediaTypeImageManifest,
   114  		Subject:   &subjectDescriptor,
   115  		Config:    ocispec.DescriptorEmptyJSON,
   116  	})
   117  	referrerDescriptor = content.NewDescriptorFromBytes(ocispec.MediaTypeImageManifest, referrerManifestContent)
   118  	referrerIndex, _   = json.Marshal(ocispec.Index{
   119  		Manifests: []ocispec.Descriptor{},
   120  	})
   121  )
   122  
   123  var host string
   124  
   125  func TestMain(m *testing.M) {
   126  	// Setup a local HTTPS registry
   127  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   128  		p := r.URL.Path
   129  		m := r.Method
   130  		switch {
   131  		case p == "/v2/_catalog" && m == "GET":
   132  			result := struct {
   133  				Repositories []string `json:"repositories"`
   134  			}{
   135  				Repositories: []string{"public/repo1", "public/repo2", "internal/repo3"},
   136  			}
   137  			json.NewEncoder(w).Encode(result)
   138  		case p == fmt.Sprintf("/v2/%s/tags/list", exampleRepositoryName) && m == "GET":
   139  			result := struct {
   140  				Tags []string `json:"tags"`
   141  			}{
   142  				Tags: []string{"tag1", "tag2"},
   143  			}
   144  			json.NewEncoder(w).Encode(result)
   145  		case p == fmt.Sprintf("/v2/%s/blobs/uploads/", exampleRepositoryName):
   146  			w.Header().Set("Location", p+exampleUploadUUid)
   147  			w.Header().Set("Docker-Upload-UUID", exampleUploadUUid)
   148  			w.WriteHeader(http.StatusAccepted)
   149  		case p == fmt.Sprintf("/v2/%s/blobs/uploads/%s", exampleRepositoryName, exampleUploadUUid):
   150  			w.WriteHeader(http.StatusCreated)
   151  		case p == fmt.Sprintf("/v2/%s/manifests/%s", exampleRepositoryName, exampleTag) && m == "PUT":
   152  			w.WriteHeader(http.StatusCreated)
   153  		case p == fmt.Sprintf("/v2/%s/manifests/%s", exampleRepositoryName, ManifestDigest) && m == "PUT":
   154  			w.WriteHeader(http.StatusCreated)
   155  		case p == fmt.Sprintf("/v2/%s/manifests/%s", exampleRepositoryName, ReferenceManifestDigest) && m == "PUT":
   156  			w.Header().Set("OCI-Subject", "sha256:a3f9d449466b9b7194c3a76ca4890d792e11eb4e62e59aa8b4c3cce0a56f129d")
   157  			w.WriteHeader(http.StatusCreated)
   158  		case p == fmt.Sprintf("/v2/%s/manifests/%s", exampleRepositoryName, exampleSignatureManifestDescriptor.Digest) && m == "GET":
   159  			w.Header().Set("Content-Type", spec.MediaTypeArtifactManifest)
   160  			w.Header().Set("Content-Digest", string(exampleSignatureManifestDescriptor.Digest))
   161  			w.Header().Set("Content-Length", strconv.Itoa(len(exampleSignatureManifest)))
   162  			w.Write(exampleSignatureManifest)
   163  		case p == fmt.Sprintf("/v2/%s/manifests/%s", exampleRepositoryName, exampleSBoMManifestDescriptor.Digest) && m == "GET":
   164  			w.Header().Set("Content-Type", spec.MediaTypeArtifactManifest)
   165  			w.Header().Set("Content-Digest", string(exampleSBoMManifestDescriptor.Digest))
   166  			w.Header().Set("Content-Length", strconv.Itoa(len(exampleSBoMManifest)))
   167  			w.Write(exampleSBoMManifest)
   168  		case p == fmt.Sprintf("/v2/%s/manifests/%s", exampleRepositoryName, exampleManifestWithBlobsDescriptor.Digest) && m == "GET":
   169  			w.Header().Set("Content-Type", spec.MediaTypeArtifactManifest)
   170  			w.Header().Set("Content-Digest", string(exampleManifestWithBlobsDescriptor.Digest))
   171  			w.Header().Set("Content-Length", strconv.Itoa(len(exampleManifestWithBlobs)))
   172  			w.Write(exampleManifestWithBlobs)
   173  		case p == fmt.Sprintf("/v2/%s/blobs/%s", exampleRepositoryName, blobDescriptor.Digest) && m == "GET":
   174  			w.Header().Set("Content-Type", spec.MediaTypeArtifactManifest)
   175  			w.Header().Set("Content-Digest", string(blobDescriptor.Digest))
   176  			w.Header().Set("Content-Length", strconv.Itoa(len(blobContent)))
   177  			w.Write([]byte(blobContent))
   178  		case p == fmt.Sprintf("/v2/%s/referrers/%s", exampleRepositoryName, "sha256:0000000000000000000000000000000000000000000000000000000000000000"):
   179  			result := ocispec.Index{
   180  				Versioned: specs.Versioned{
   181  					SchemaVersion: 2, // historical value. does not pertain to OCI or docker version
   182  				},
   183  				MediaType: ocispec.MediaTypeImageIndex,
   184  			}
   185  			w.Header().Set("Content-Type", ocispec.MediaTypeImageIndex)
   186  			if err := json.NewEncoder(w).Encode(result); err != nil {
   187  				panic(err)
   188  			}
   189  		case p == fmt.Sprintf("/v2/%s/referrers/%s", exampleRepositoryName, exampleManifestDescriptor.Digest.String()):
   190  			q := r.URL.Query()
   191  			var referrers []ocispec.Descriptor
   192  			switch q.Get("test") {
   193  			case "page1":
   194  				referrers = exampleReferrerDescriptors[1]
   195  			default:
   196  				referrers = exampleReferrerDescriptors[0]
   197  				w.Header().Set("Link", fmt.Sprintf(`<%s?n=1&test=page1>; rel="next"`, p))
   198  			}
   199  			result := ocispec.Index{
   200  				Versioned: specs.Versioned{
   201  					SchemaVersion: 2, // historical value. does not pertain to OCI or docker version
   202  				},
   203  				MediaType: ocispec.MediaTypeImageIndex,
   204  				Manifests: referrers,
   205  			}
   206  			w.Header().Set("Content-Type", ocispec.MediaTypeImageIndex)
   207  			if err := json.NewEncoder(w).Encode(result); err != nil {
   208  				panic(err)
   209  			}
   210  		case p == fmt.Sprintf("/v2/%s/manifests/%s", exampleRepositoryName, exampleTag) || p == fmt.Sprintf("/v2/%s/manifests/%s", exampleRepositoryName, exampleManifestDigest):
   211  			w.Header().Set("Content-Type", ocispec.MediaTypeImageManifest)
   212  			w.Header().Set("Docker-Content-Digest", exampleManifestDigest)
   213  			w.Header().Set("Content-Length", strconv.Itoa(len([]byte(exampleManifest))))
   214  			w.Header().Set("Warning", `299 - "This image is deprecated and will be removed soon."`)
   215  			if m == "GET" {
   216  				w.Write([]byte(exampleManifest))
   217  			}
   218  		case p == fmt.Sprintf("/v2/%s/blobs/%s", exampleRepositoryName, exampleLayerDigest):
   219  			w.Header().Set("Content-Type", ocispec.MediaTypeImageLayer)
   220  			w.Header().Set("Docker-Content-Digest", string(exampleLayerDigest))
   221  			w.Header().Set("Accept-Ranges", "bytes")
   222  			var start, end = 0, len(exampleLayer) - 1
   223  			if h := r.Header.Get("Range"); h != "" {
   224  				w.WriteHeader(http.StatusPartialContent)
   225  				indices := strings.Split(strings.Split(h, "=")[1], "-")
   226  				var err error
   227  				start, err = strconv.Atoi(indices[0])
   228  				if err != nil {
   229  					panic(err)
   230  				}
   231  				end, err = strconv.Atoi(indices[1])
   232  				if err != nil {
   233  					panic(err)
   234  				}
   235  			}
   236  			resultBlob := exampleLayer[start : end+1]
   237  			w.Header().Set("Content-Length", strconv.Itoa(len([]byte(resultBlob))))
   238  			if m == "GET" {
   239  				w.Write([]byte(resultBlob))
   240  			}
   241  		case p == fmt.Sprintf("/v2/%s/referrers/%s", referrersAPIUnavailableRepositoryName, "sha256:0000000000000000000000000000000000000000000000000000000000000000"):
   242  			w.WriteHeader(http.StatusNotFound)
   243  		case p == fmt.Sprintf("/v2/%s/manifests/%s", referrersAPIUnavailableRepositoryName, referrerDigest) && m == http.MethodPut:
   244  			w.WriteHeader(http.StatusCreated)
   245  		case p == fmt.Sprintf("/v2/%s/manifests/%s", referrersAPIUnavailableRepositoryName, referrersTag) && m == http.MethodGet:
   246  			w.Write(referrerIndex)
   247  			w.Header().Set("Content-Type", ocispec.MediaTypeImageIndex)
   248  			w.Header().Set("Content-Length", strconv.Itoa(len(referrerIndex)))
   249  			w.Header().Set("Docker-Content-Digest", digest.Digest(string(referrerIndex)).String())
   250  			w.WriteHeader(http.StatusCreated)
   251  		case p == fmt.Sprintf("/v2/%s/manifests/%s", referrersAPIUnavailableRepositoryName, referrersTag) && m == http.MethodPut:
   252  			w.WriteHeader(http.StatusCreated)
   253  		case p == fmt.Sprintf("/v2/%s/manifests/%s", referrersAPIUnavailableRepositoryName, referrerIndexDigest) && m == http.MethodDelete:
   254  			w.WriteHeader(http.StatusMethodNotAllowed)
   255  		}
   256  
   257  	}))
   258  	defer ts.Close()
   259  	u, err := url.Parse(ts.URL)
   260  	if err != nil {
   261  		panic(err)
   262  	}
   263  	host = u.Host
   264  	http.DefaultTransport = ts.Client().Transport
   265  
   266  	os.Exit(m.Run())
   267  }
   268  
   269  // ExampleRepository_Tags gives example snippets for listing tags in a repository.
   270  func ExampleRepository_Tags() {
   271  	repo, err := remote.NewRepository(fmt.Sprintf("%s/%s", host, exampleRepositoryName))
   272  	if err != nil {
   273  		panic(err)
   274  	}
   275  	ctx := context.Background()
   276  	err = repo.Tags(ctx, "", func(tags []string) error {
   277  		for _, tag := range tags {
   278  			fmt.Println(tag)
   279  		}
   280  		return nil
   281  	})
   282  
   283  	if err != nil {
   284  		panic(err)
   285  	}
   286  
   287  	// Output:
   288  	// tag1
   289  	// tag2
   290  }
   291  
   292  // ExampleRepository_Push gives example snippets for pushing a layer.
   293  func ExampleRepository_Push() {
   294  	repo, err := remote.NewRepository(fmt.Sprintf("%s/%s", host, exampleRepositoryName))
   295  	if err != nil {
   296  		panic(err)
   297  	}
   298  	ctx := context.Background()
   299  
   300  	// 1. assemble a descriptor
   301  	layer := []byte("Example layer content")
   302  	descriptor := content.NewDescriptorFromBytes(ocispec.MediaTypeImageLayer, layer)
   303  	// 2. push the descriptor and blob content
   304  	err = repo.Push(ctx, descriptor, bytes.NewReader(layer))
   305  	if err != nil {
   306  		panic(err)
   307  	}
   308  
   309  	fmt.Println("Push finished")
   310  	// Output:
   311  	// Push finished
   312  }
   313  
   314  // ExampleRepository_Push_artifactReferenceManifest gives an example snippet for pushing a reference manifest.
   315  func ExampleRepository_Push_artifactReferenceManifest() {
   316  	repo, err := remote.NewRepository(fmt.Sprintf("%s/%s", host, exampleRepositoryName))
   317  	if err != nil {
   318  		panic(err)
   319  	}
   320  	ctx := context.Background()
   321  
   322  	// 1. assemble the referenced artifact manifest
   323  	manifest := ocispec.Manifest{
   324  		Versioned: specs.Versioned{
   325  			SchemaVersion: 2, // historical value. does not pertain to OCI or docker version
   326  		},
   327  		MediaType: ocispec.MediaTypeImageManifest,
   328  		Config:    content.NewDescriptorFromBytes(ocispec.MediaTypeImageConfig, []byte("config bytes")),
   329  	}
   330  	manifestContent, err := json.Marshal(manifest)
   331  	if err != nil {
   332  		panic(err)
   333  	}
   334  	manifestDescriptor := content.NewDescriptorFromBytes(ocispec.MediaTypeImageManifest, manifestContent)
   335  
   336  	// 2. push the manifest descriptor and content
   337  	err = repo.Push(ctx, manifestDescriptor, bytes.NewReader(manifestContent))
   338  	if err != nil {
   339  		panic(err)
   340  	}
   341  
   342  	// 3. assemble the reference artifact manifest
   343  	referenceManifest := spec.Artifact{
   344  		MediaType:    spec.MediaTypeArtifactManifest,
   345  		ArtifactType: "sbom/example",
   346  		Subject:      &manifestDescriptor,
   347  	}
   348  	referenceManifestContent, err := json.Marshal(referenceManifest)
   349  	if err != nil {
   350  		panic(err)
   351  	}
   352  	referenceManifestDescriptor := content.NewDescriptorFromBytes(spec.MediaTypeArtifactManifest, referenceManifestContent)
   353  	// 4. push the reference manifest descriptor and content
   354  	err = repo.Push(ctx, referenceManifestDescriptor, bytes.NewReader(referenceManifestContent))
   355  	if err != nil {
   356  		panic(err)
   357  	}
   358  
   359  	fmt.Println("Push finished")
   360  	// Output:
   361  	// Push finished
   362  }
   363  
   364  // ExampleRepository_Resolve_byTag gives example snippets for resolving a tag to a manifest descriptor.
   365  func ExampleRepository_Resolve_byTag() {
   366  	repo, err := remote.NewRepository(fmt.Sprintf("%s/%s", host, exampleRepositoryName))
   367  	if err != nil {
   368  		panic(err)
   369  	}
   370  	ctx := context.Background()
   371  
   372  	tag := "latest"
   373  	descriptor, err := repo.Resolve(ctx, tag)
   374  	if err != nil {
   375  		panic(err)
   376  	}
   377  
   378  	fmt.Println(descriptor.MediaType)
   379  	fmt.Println(descriptor.Digest)
   380  	fmt.Println(descriptor.Size)
   381  
   382  	// Output:
   383  	// application/vnd.oci.image.manifest.v1+json
   384  	// sha256:b53dc03a49f383ba230d8ac2b78a9c4aec132e4a9f36cc96524df98163202cc7
   385  	// 337
   386  }
   387  
   388  // ExampleRepository_Resolve_byDigest gives example snippets for resolving a digest to a manifest descriptor.
   389  func ExampleRepository_Resolve_byDigest() {
   390  	repo, err := remote.NewRepository(fmt.Sprintf("%s/%s", host, exampleRepositoryName))
   391  	if err != nil {
   392  		panic(err)
   393  	}
   394  	ctx := context.Background()
   395  	exampleDigest := "sha256:b53dc03a49f383ba230d8ac2b78a9c4aec132e4a9f36cc96524df98163202cc7"
   396  	descriptor, err := repo.Resolve(ctx, exampleDigest)
   397  	if err != nil {
   398  		panic(err)
   399  	}
   400  
   401  	fmt.Println(descriptor.MediaType)
   402  	fmt.Println(descriptor.Digest)
   403  	fmt.Println(descriptor.Size)
   404  
   405  	// Output:
   406  	// application/vnd.oci.image.manifest.v1+json
   407  	// sha256:b53dc03a49f383ba230d8ac2b78a9c4aec132e4a9f36cc96524df98163202cc7
   408  	// 337
   409  }
   410  
   411  // ExampleRepository_Fetch_byTag gives example snippets for downloading a manifest by tag.
   412  func ExampleRepository_Fetch_manifestByTag() {
   413  	repo, err := remote.NewRepository(fmt.Sprintf("%s/%s", host, exampleRepositoryName))
   414  	if err != nil {
   415  		panic(err)
   416  	}
   417  	ctx := context.Background()
   418  
   419  	tag := "latest"
   420  	descriptor, err := repo.Resolve(ctx, tag)
   421  	if err != nil {
   422  		panic(err)
   423  	}
   424  	rc, err := repo.Fetch(ctx, descriptor)
   425  	if err != nil {
   426  		panic(err)
   427  	}
   428  	defer rc.Close() // don't forget to close
   429  	pulledBlob, err := content.ReadAll(rc, descriptor)
   430  	if err != nil {
   431  		panic(err)
   432  	}
   433  
   434  	fmt.Println(string(pulledBlob))
   435  
   436  	// Output:
   437  	// {"schemaVersion":2,"config":{"mediaType":"application/vnd.oci.image.config.v1+json","digest":"sha256:569224ae188c06e97b9fcadaeb2358fb0fb7c4eb105d49aee2620b2719abea43","size":22},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar","digest":"sha256:ef79e47691ad1bc702d7a256da6323ec369a8fc3159b4f1798a47136f3b38c10","size":21}]}
   438  }
   439  
   440  // ExampleRepository_Fetch_manifestByDigest gives example snippets for downloading a manifest by digest.
   441  func ExampleRepository_Fetch_manifestByDigest() {
   442  	repo, err := remote.NewRepository(fmt.Sprintf("%s/%s", host, exampleRepositoryName))
   443  	if err != nil {
   444  		panic(err)
   445  	}
   446  	ctx := context.Background()
   447  
   448  	exampleDigest := "sha256:b53dc03a49f383ba230d8ac2b78a9c4aec132e4a9f36cc96524df98163202cc7"
   449  	// resolve the blob descriptor to obtain the size of the blob
   450  	descriptor, err := repo.Resolve(ctx, exampleDigest)
   451  	if err != nil {
   452  		panic(err)
   453  	}
   454  	rc, err := repo.Fetch(ctx, descriptor)
   455  	if err != nil {
   456  		panic(err)
   457  	}
   458  	defer rc.Close() // don't forget to close
   459  	pulled, err := content.ReadAll(rc, descriptor)
   460  	if err != nil {
   461  		panic(err)
   462  	}
   463  
   464  	fmt.Println(string(pulled))
   465  	// Output:
   466  	// {"schemaVersion":2,"config":{"mediaType":"application/vnd.oci.image.config.v1+json","digest":"sha256:569224ae188c06e97b9fcadaeb2358fb0fb7c4eb105d49aee2620b2719abea43","size":22},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar","digest":"sha256:ef79e47691ad1bc702d7a256da6323ec369a8fc3159b4f1798a47136f3b38c10","size":21}]}
   467  }
   468  
   469  // ExampleRepository_Fetch_artifactReferenceManifest gives an example of fetching
   470  // the referrers of a given manifest by using the Referrers API.
   471  func ExampleRepository_Fetch_artifactReferenceManifest() {
   472  	repo, err := remote.NewRepository(fmt.Sprintf("%s/%s", host, exampleRepositoryName))
   473  	if err != nil {
   474  		panic(err)
   475  	}
   476  	ctx := context.Background()
   477  
   478  	// resolve a manifest by tag
   479  	tag := "latest"
   480  	descriptor, err := repo.Resolve(ctx, tag)
   481  	if err != nil {
   482  		panic(err)
   483  	}
   484  	// find its referrers by calling Referrers
   485  	if err := repo.Referrers(ctx, descriptor, "", func(referrers []ocispec.Descriptor) error {
   486  		// for each page of the results, do the following:
   487  		for _, referrer := range referrers {
   488  			// for each item in this page, pull the manifest and verify its content
   489  			rc, err := repo.Fetch(ctx, referrer)
   490  			if err != nil {
   491  				panic(err)
   492  			}
   493  			defer rc.Close() // don't forget to close
   494  			pulledBlob, err := content.ReadAll(rc, referrer)
   495  			if err != nil {
   496  				panic(err)
   497  			}
   498  			fmt.Println(string(pulledBlob))
   499  		}
   500  		return nil
   501  	}); err != nil {
   502  		panic(err)
   503  	}
   504  	// Output:
   505  	// {"mediaType":"application/vnd.oci.artifact.manifest.v1+json","artifactType":"example/SBoM","subject":{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:b53dc03a49f383ba230d8ac2b78a9c4aec132e4a9f36cc96524df98163202cc7","size":337}}
   506  	// {"mediaType":"application/vnd.oci.artifact.manifest.v1+json","artifactType":"example/signature","subject":{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:b53dc03a49f383ba230d8ac2b78a9c4aec132e4a9f36cc96524df98163202cc7","size":337}}
   507  }
   508  
   509  // ExampleRepository_fetchArtifactBlobs gives an example of pulling the blobs
   510  // of an artifact manifest.
   511  func ExampleRepository_fetchArtifactBlobs() {
   512  	repo, err := remote.NewRepository(fmt.Sprintf("%s/%s", host, exampleRepositoryName))
   513  	if err != nil {
   514  		panic(err)
   515  	}
   516  	ctx := context.Background()
   517  
   518  	// 1. Fetch the artifact manifest by digest.
   519  	exampleDigest := "sha256:f3550fd0947402d140fd0470702abc92c69f7e9b08d5ca2438f42f8a0ea3fd97"
   520  	descriptor, rc, err := repo.FetchReference(ctx, exampleDigest)
   521  	if err != nil {
   522  		panic(err)
   523  	}
   524  	defer rc.Close()
   525  
   526  	pulledContent, err := content.ReadAll(rc, descriptor)
   527  	if err != nil {
   528  		panic(err)
   529  	}
   530  	fmt.Println(string(pulledContent))
   531  
   532  	// 2. Parse the pulled manifest and fetch its blobs.
   533  	var pulledManifest spec.Artifact
   534  	if err := json.Unmarshal(pulledContent, &pulledManifest); err != nil {
   535  		panic(err)
   536  	}
   537  	for _, blob := range pulledManifest.Blobs {
   538  		content, err := content.FetchAll(ctx, repo, blob)
   539  		if err != nil {
   540  			panic(err)
   541  		}
   542  		fmt.Println(string(content))
   543  	}
   544  
   545  	// Output:
   546  	// {"mediaType":"application/vnd.oci.artifact.manifest.v1+json","artifactType":"example/manifest","blobs":[{"mediaType":"application/tar","digest":"sha256:8d6497c94694a292c04f85cd055d8b5c03eda835dd311e20dfbbf029ff9748cc","size":20}],"subject":{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:b53dc03a49f383ba230d8ac2b78a9c4aec132e4a9f36cc96524df98163202cc7","size":337}}
   547  	// example blob content
   548  }
   549  
   550  // ExampleRepository_FetchReference_manifestByTag gives example snippets for downloading a manifest by tag with only one API call.
   551  func ExampleRepository_FetchReference_manifestByTag() {
   552  	repo, err := remote.NewRepository(fmt.Sprintf("%s/%s", host, exampleRepositoryName))
   553  	if err != nil {
   554  		panic(err)
   555  	}
   556  	ctx := context.Background()
   557  
   558  	tag := "latest"
   559  	descriptor, rc, err := repo.FetchReference(ctx, tag)
   560  	if err != nil {
   561  		panic(err)
   562  	}
   563  	defer rc.Close() // don't forget to close
   564  	pulledBlob, err := content.ReadAll(rc, descriptor)
   565  	if err != nil {
   566  		panic(err)
   567  	}
   568  
   569  	fmt.Println(string(pulledBlob))
   570  
   571  	// Output:
   572  	// {"schemaVersion":2,"config":{"mediaType":"application/vnd.oci.image.config.v1+json","digest":"sha256:569224ae188c06e97b9fcadaeb2358fb0fb7c4eb105d49aee2620b2719abea43","size":22},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar","digest":"sha256:ef79e47691ad1bc702d7a256da6323ec369a8fc3159b4f1798a47136f3b38c10","size":21}]}
   573  }
   574  
   575  // ExampleRepository_FetchReference_manifestByDigest gives example snippets for downloading a manifest by digest.
   576  func ExampleRepository_FetchReference_manifestByDigest() {
   577  	repo, err := remote.NewRepository(fmt.Sprintf("%s/%s", host, exampleRepositoryName))
   578  	if err != nil {
   579  		panic(err)
   580  	}
   581  	ctx := context.Background()
   582  
   583  	exampleDigest := "sha256:b53dc03a49f383ba230d8ac2b78a9c4aec132e4a9f36cc96524df98163202cc7"
   584  	descriptor, rc, err := repo.FetchReference(ctx, exampleDigest)
   585  	if err != nil {
   586  		panic(err)
   587  	}
   588  	defer rc.Close() // don't forget to close
   589  	pulled, err := content.ReadAll(rc, descriptor)
   590  	if err != nil {
   591  		panic(err)
   592  	}
   593  
   594  	fmt.Println(string(pulled))
   595  
   596  	// Output:
   597  	// {"schemaVersion":2,"config":{"mediaType":"application/vnd.oci.image.config.v1+json","digest":"sha256:569224ae188c06e97b9fcadaeb2358fb0fb7c4eb105d49aee2620b2719abea43","size":22},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar","digest":"sha256:ef79e47691ad1bc702d7a256da6323ec369a8fc3159b4f1798a47136f3b38c10","size":21}]}
   598  }
   599  
   600  // ExampleRepository_Fetch_layer gives example snippets for downloading a layer blob by digest.
   601  func ExampleRepository_Fetch_layer() {
   602  	repo, err := remote.NewRepository(fmt.Sprintf("%s/%s", host, exampleRepositoryName))
   603  	if err != nil {
   604  		panic(err)
   605  	}
   606  	ctx := context.Background()
   607  
   608  	descriptor, err := repo.Blobs().Resolve(ctx, exampleLayerDigest)
   609  	if err != nil {
   610  		panic(err)
   611  	}
   612  	rc, err := repo.Fetch(ctx, descriptor)
   613  	if err != nil {
   614  		panic(err)
   615  	}
   616  	defer rc.Close() // don't forget to close
   617  
   618  	// option 1: sequential fetch
   619  	pulledBlob, err := content.ReadAll(rc, descriptor)
   620  	if err != nil {
   621  		panic(err)
   622  	}
   623  	fmt.Println(string(pulledBlob))
   624  
   625  	// option 2: random access, if the remote registry supports
   626  	if seeker, ok := rc.(io.ReadSeeker); ok {
   627  		offset := int64(8)
   628  		_, err = seeker.Seek(offset, io.SeekStart)
   629  		if err != nil {
   630  			panic(err)
   631  		}
   632  		pulledBlob, err := io.ReadAll(rc)
   633  		if err != nil {
   634  			panic(err)
   635  		}
   636  		if descriptor.Size-offset != int64(len(pulledBlob)) {
   637  			panic("wrong content")
   638  		}
   639  		fmt.Println(string(pulledBlob))
   640  	}
   641  
   642  	// Output:
   643  	// Example layer content
   644  	// layer content
   645  }
   646  
   647  // ExampleRepository_Tag gives example snippets for tagging a descriptor.
   648  func ExampleRepository_Tag() {
   649  	repo, err := remote.NewRepository(fmt.Sprintf("%s/%s", host, exampleRepositoryName))
   650  	if err != nil {
   651  		panic(err)
   652  	}
   653  	ctx := context.Background()
   654  
   655  	exampleDigest := "sha256:b53dc03a49f383ba230d8ac2b78a9c4aec132e4a9f36cc96524df98163202cc7"
   656  	descriptor, err := repo.Resolve(ctx, exampleDigest)
   657  	if err != nil {
   658  		panic(err)
   659  	}
   660  	tag := "latest"
   661  	err = repo.Tag(ctx, descriptor, tag)
   662  	if err != nil {
   663  		panic(err)
   664  	}
   665  	fmt.Println("Succeed")
   666  
   667  	// Output:
   668  	// Succeed
   669  }
   670  
   671  // ExampleRegistry_Repositories gives example snippets for listing respositories in a HTTPS registry with pagination.
   672  func ExampleRegistry_Repositories() {
   673  	reg, err := remote.NewRegistry(host)
   674  	if err != nil {
   675  		panic(err)
   676  	}
   677  	// Override the `host` variable to play with local registry.
   678  	// Uncomment below line to reset HTTP option:
   679  	// reg.PlainHTTP = true
   680  	ctx := context.Background()
   681  	err = reg.Repositories(ctx, "", func(repos []string) error {
   682  		for _, repo := range repos {
   683  			fmt.Println(repo)
   684  		}
   685  		return nil
   686  	})
   687  	if err != nil {
   688  		panic(err)
   689  	}
   690  
   691  	// Output:
   692  	// public/repo1
   693  	// public/repo2
   694  	// internal/repo3
   695  }
   696  
   697  func Example_pullByTag() {
   698  	repo, err := remote.NewRepository(fmt.Sprintf("%s/%s", host, exampleRepositoryName))
   699  	if err != nil {
   700  		panic(err)
   701  	}
   702  	ctx := context.Background()
   703  
   704  	// 1. resolve the descriptor
   705  	tag := "latest"
   706  	descriptor, err := repo.Resolve(ctx, tag)
   707  	if err != nil {
   708  		panic(err)
   709  	}
   710  	fmt.Println(descriptor.Digest)
   711  	fmt.Println(descriptor.Size)
   712  	// 2. fetch the content byte[] from the repository
   713  	pulledBlob, err := content.FetchAll(ctx, repo, descriptor)
   714  	if err != nil {
   715  		panic(err)
   716  	}
   717  
   718  	fmt.Println(string(pulledBlob))
   719  
   720  	// Output:
   721  	// sha256:b53dc03a49f383ba230d8ac2b78a9c4aec132e4a9f36cc96524df98163202cc7
   722  	// 337
   723  	// {"schemaVersion":2,"config":{"mediaType":"application/vnd.oci.image.config.v1+json","digest":"sha256:569224ae188c06e97b9fcadaeb2358fb0fb7c4eb105d49aee2620b2719abea43","size":22},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar","digest":"sha256:ef79e47691ad1bc702d7a256da6323ec369a8fc3159b4f1798a47136f3b38c10","size":21}]}
   724  }
   725  
   726  func Example_pullByDigest() {
   727  	repo, err := remote.NewRepository(fmt.Sprintf("%s/%s", host, exampleRepositoryName))
   728  	if err != nil {
   729  		panic(err)
   730  	}
   731  	ctx := context.Background()
   732  
   733  	exampleDigest := "sha256:b53dc03a49f383ba230d8ac2b78a9c4aec132e4a9f36cc96524df98163202cc7"
   734  	// 1. resolve the descriptor
   735  	descriptor, err := repo.Resolve(ctx, exampleDigest)
   736  	if err != nil {
   737  		panic(err)
   738  	}
   739  	fmt.Println(descriptor.Digest)
   740  	fmt.Println(descriptor.Size)
   741  	// 2. fetch the content byte[] from the repository
   742  	pulledBlob, err := content.FetchAll(ctx, repo, descriptor)
   743  	if err != nil {
   744  		panic(err)
   745  	}
   746  
   747  	fmt.Println(string(pulledBlob))
   748  
   749  	// Output:
   750  	// sha256:b53dc03a49f383ba230d8ac2b78a9c4aec132e4a9f36cc96524df98163202cc7
   751  	// 337
   752  	// {"schemaVersion":2,"config":{"mediaType":"application/vnd.oci.image.config.v1+json","digest":"sha256:569224ae188c06e97b9fcadaeb2358fb0fb7c4eb105d49aee2620b2719abea43","size":22},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar","digest":"sha256:ef79e47691ad1bc702d7a256da6323ec369a8fc3159b4f1798a47136f3b38c10","size":21}]}
   753  }
   754  
   755  func Example_handleWarning() {
   756  	repo, err := remote.NewRepository(fmt.Sprintf("%s/%s", host, exampleRepositoryName))
   757  	if err != nil {
   758  		panic(err)
   759  	}
   760  	// 1. specify HandleWarning
   761  	repo.HandleWarning = func(warning remote.Warning) {
   762  		fmt.Printf("Warning from %s: %s\n", repo.Reference.Repository, warning.Text)
   763  	}
   764  
   765  	ctx := context.Background()
   766  	exampleDigest := "sha256:b53dc03a49f383ba230d8ac2b78a9c4aec132e4a9f36cc96524df98163202cc7"
   767  	// 2. resolve the descriptor
   768  	descriptor, err := repo.Resolve(ctx, exampleDigest)
   769  	if err != nil {
   770  		panic(err)
   771  	}
   772  	fmt.Println(descriptor.Digest)
   773  	fmt.Println(descriptor.Size)
   774  
   775  	// 3. fetch the content byte[] from the repository
   776  	pulledBlob, err := content.FetchAll(ctx, repo, descriptor)
   777  	if err != nil {
   778  		panic(err)
   779  	}
   780  	fmt.Println(string(pulledBlob))
   781  
   782  	// Output:
   783  	// Warning from example: This image is deprecated and will be removed soon.
   784  	// sha256:b53dc03a49f383ba230d8ac2b78a9c4aec132e4a9f36cc96524df98163202cc7
   785  	// 337
   786  	// Warning from example: This image is deprecated and will be removed soon.
   787  	// {"schemaVersion":2,"config":{"mediaType":"application/vnd.oci.image.config.v1+json","digest":"sha256:569224ae188c06e97b9fcadaeb2358fb0fb7c4eb105d49aee2620b2719abea43","size":22},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar","digest":"sha256:ef79e47691ad1bc702d7a256da6323ec369a8fc3159b4f1798a47136f3b38c10","size":21}]}
   788  }
   789  
   790  // Example_pushAndTag gives example snippet of pushing an OCI image with a tag.
   791  func Example_pushAndTag() {
   792  	repo, err := remote.NewRepository(fmt.Sprintf("%s/%s", host, exampleRepositoryName))
   793  	if err != nil {
   794  		panic(err)
   795  	}
   796  	ctx := context.Background()
   797  
   798  	// Assemble the below OCI image, push and tag it
   799  	//   +---------------------------------------------------+
   800  	//   |                                +----------------+ |
   801  	//   |                             +--> "Hello Config" | |
   802  	//   |            +-------------+  |  +---+ Config +---+ |
   803  	//   | (latest)+-->     ...     +--+                     |
   804  	//   |            ++ Manifest  ++  |  +----------------+ |
   805  	//   |                             +--> "Hello Layer"  | |
   806  	//   |                                +---+ Layer  +---+ |
   807  	//   |                                                   |
   808  	//   +--------+ localhost:5000/example/registry +--------+
   809  
   810  	generateManifest := func(config ocispec.Descriptor, layers ...ocispec.Descriptor) ([]byte, error) {
   811  		content := ocispec.Manifest{
   812  			Config:    config,
   813  			Layers:    layers,
   814  			Versioned: specs.Versioned{SchemaVersion: 2},
   815  		}
   816  		return json.Marshal(content)
   817  	}
   818  	// 1. assemble descriptors and manifest
   819  	layerBlob := []byte("Hello layer")
   820  	layerDesc := content.NewDescriptorFromBytes(ocispec.MediaTypeImageLayer, layerBlob)
   821  	configBlob := []byte("Hello config")
   822  	configDesc := content.NewDescriptorFromBytes(ocispec.MediaTypeImageConfig, configBlob)
   823  	manifestBlob, err := generateManifest(configDesc, layerDesc)
   824  	if err != nil {
   825  		panic(err)
   826  	}
   827  	manifestDesc := content.NewDescriptorFromBytes(ocispec.MediaTypeImageManifest, manifestBlob)
   828  
   829  	// 2. push and tag
   830  	err = repo.Push(ctx, layerDesc, bytes.NewReader(layerBlob))
   831  	if err != nil {
   832  		panic(err)
   833  	}
   834  	err = repo.Push(ctx, configDesc, bytes.NewReader(configBlob))
   835  	if err != nil {
   836  		panic(err)
   837  	}
   838  	err = repo.PushReference(ctx, manifestDesc, bytes.NewReader(manifestBlob), "latest")
   839  	if err != nil {
   840  		panic(err)
   841  	}
   842  
   843  	fmt.Println("Succeed")
   844  
   845  	// Output:
   846  	// Succeed
   847  }
   848  
   849  // Example_tagReference gives example snippets for tagging
   850  // a manifest.
   851  func Example_tagReference() {
   852  	reg, err := remote.NewRegistry(host)
   853  	if err != nil {
   854  		panic(err)
   855  	}
   856  	ctx := context.Background()
   857  	repo, err := reg.Repository(ctx, exampleRepositoryName)
   858  	if err != nil {
   859  		panic(err)
   860  	}
   861  
   862  	// tag a manifest referenced by the exampleDigest below
   863  	exampleDigest := "sha256:b53dc03a49f383ba230d8ac2b78a9c4aec132e4a9f36cc96524df98163202cc7"
   864  	tag := "latest"
   865  	desc, err := oras.Tag(ctx, repo, exampleDigest, tag)
   866  	if err != nil {
   867  		panic(err)
   868  	}
   869  	fmt.Println("Tagged", desc.Digest, "as", tag)
   870  
   871  	// Output:
   872  	// Tagged sha256:b53dc03a49f383ba230d8ac2b78a9c4aec132e4a9f36cc96524df98163202cc7 as latest
   873  }
   874  
   875  // Example_pushAndIgnoreReferrersIndexError gives example snippets on how to
   876  // ignore referrer index deletion error during push a referrer manifest.
   877  func Example_pushAndIgnoreReferrersIndexError() {
   878  	repo, err := remote.NewRepository(fmt.Sprintf("%s/%s", host, referrersAPIUnavailableRepositoryName))
   879  	if err != nil {
   880  		panic(err)
   881  	}
   882  	ctx := context.Background()
   883  
   884  	// push a referrer manifest and ignore cleaning up error
   885  	err = repo.Push(ctx, referrerDescriptor, bytes.NewReader(referrerManifestContent))
   886  	if err != nil {
   887  		var re *remote.ReferrersError
   888  		if !errors.As(err, &re) || !re.IsReferrersIndexDelete() {
   889  			panic(err)
   890  		}
   891  		fmt.Println("ignoring error occurred during cleaning obsolete referrers index")
   892  	}
   893  	fmt.Println("Push finished")
   894  
   895  	// Output:
   896  	// ignoring error occurred during cleaning obsolete referrers index
   897  	// Push finished
   898  }