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