github.com/opcr-io/oras-go/v2@v2.0.0-20231122155130-eb4260d8a0ae/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  
     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  
    16  package oras_test
    17  
    18  import (
    19  	"bytes"
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    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/memory"
    33  	"github.com/opcr-io/oras-go/v2/content/oci"
    34  	"github.com/opcr-io/oras-go/v2/registry/remote"
    35  	"github.com/opencontainers/go-digest"
    36  	specs "github.com/opencontainers/image-spec/specs-go"
    37  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    38  )
    39  
    40  var exampleMemoryStore oras.Target
    41  var remoteHost string
    42  var (
    43  	exampleManifest, _ = json.Marshal(ocispec.Artifact{
    44  		MediaType:    ocispec.MediaTypeArtifactManifest,
    45  		ArtifactType: "example/content"})
    46  	exampleManifestDescriptor = ocispec.Descriptor{
    47  		MediaType: ocispec.MediaTypeArtifactManifest,
    48  		Digest:    digest.Digest(digest.FromBytes(exampleManifest)),
    49  		Size:      int64(len(exampleManifest))}
    50  	exampleSignatureManifest, _ = json.Marshal(ocispec.Artifact{
    51  		MediaType:    ocispec.MediaTypeArtifactManifest,
    52  		ArtifactType: "example/signature",
    53  		Subject:      &exampleManifestDescriptor})
    54  	exampleSignatureManifestDescriptor = ocispec.Descriptor{
    55  		MediaType: ocispec.MediaTypeArtifactManifest,
    56  		Digest:    digest.FromBytes(exampleSignatureManifest),
    57  		Size:      int64(len(exampleSignatureManifest))}
    58  )
    59  
    60  func pushBlob(ctx context.Context, mediaType string, blob []byte, target oras.Target) (desc ocispec.Descriptor, err error) {
    61  	desc = ocispec.Descriptor{ // Generate descriptor based on the media type and blob content
    62  		MediaType: mediaType,
    63  		Digest:    digest.FromBytes(blob), // Calculate digest
    64  		Size:      int64(len(blob)),       // Include blob size
    65  	}
    66  	return desc, target.Push(ctx, desc, bytes.NewReader(blob)) // Push the blob to the registry target
    67  }
    68  
    69  func generateManifestContent(config ocispec.Descriptor, layers ...ocispec.Descriptor) ([]byte, error) {
    70  	content := ocispec.Manifest{
    71  		Config:    config, // Set config blob
    72  		Layers:    layers, // Set layer blobs
    73  		Versioned: specs.Versioned{SchemaVersion: 2},
    74  	}
    75  	return json.Marshal(content) // Get json content
    76  }
    77  
    78  func TestMain(m *testing.M) {
    79  	const exampleTag = "latest"
    80  	const exampleUploadUUid = "0bc84d80-837c-41d9-824e-1907463c53b3"
    81  
    82  	// Setup example local target
    83  	exampleMemoryStore = memory.New()
    84  	layerBlob := []byte("Hello layer")
    85  	ctx := context.Background()
    86  	layerDesc, err := pushBlob(ctx, ocispec.MediaTypeImageLayer, layerBlob, exampleMemoryStore) // push layer blob
    87  	if err != nil {
    88  		panic(err)
    89  	}
    90  	configBlob := []byte("Hello config")
    91  	configDesc, err := pushBlob(ctx, ocispec.MediaTypeImageConfig, configBlob, exampleMemoryStore) // push config blob
    92  	if err != nil {
    93  		panic(err)
    94  	}
    95  	manifestBlob, err := generateManifestContent(configDesc, layerDesc) // generate a image manifest
    96  	if err != nil {
    97  		panic(err)
    98  	}
    99  	manifestDesc, err := pushBlob(ctx, ocispec.MediaTypeImageManifest, manifestBlob, exampleMemoryStore) // push manifest blob
   100  	if err != nil {
   101  		panic(err)
   102  	}
   103  	err = exampleMemoryStore.Tag(ctx, manifestDesc, exampleTag)
   104  	if err != nil {
   105  		panic(err)
   106  	}
   107  
   108  	// Setup example remote target
   109  	httpsServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   110  		p := r.URL.Path
   111  		m := r.Method
   112  		switch {
   113  		case strings.Contains(p, "/blobs/uploads/") && m == "POST":
   114  			w.Header().Set("Content-Type", ocispec.MediaTypeImageManifest)
   115  			w.Header().Set("Location", p+exampleUploadUUid)
   116  			w.WriteHeader(http.StatusAccepted)
   117  		case strings.Contains(p, "/blobs/uploads/"+exampleUploadUUid) && m == "GET":
   118  			w.WriteHeader(http.StatusCreated)
   119  		case strings.Contains(p, "/manifests/"+string(exampleSignatureManifestDescriptor.Digest)):
   120  			w.Header().Set("Content-Type", ocispec.MediaTypeArtifactManifest)
   121  			w.Header().Set("Docker-Content-Digest", string(exampleSignatureManifestDescriptor.Digest))
   122  			w.Header().Set("Content-Length", strconv.Itoa(len(exampleSignatureManifest)))
   123  			w.Write(exampleSignatureManifest)
   124  		case strings.Contains(p, "/manifests/latest") && m == "PUT":
   125  			w.WriteHeader(http.StatusCreated)
   126  		case strings.Contains(p, "/manifests/"+string(exampleManifestDescriptor.Digest)),
   127  			strings.Contains(p, "/manifests/latest") && m == "HEAD":
   128  			w.Header().Set("Content-Type", ocispec.MediaTypeArtifactManifest)
   129  			w.Header().Set("Docker-Content-Digest", string(exampleManifestDescriptor.Digest))
   130  			w.Header().Set("Content-Length", strconv.Itoa(len(exampleManifest)))
   131  			if m == "GET" {
   132  				w.Write(exampleManifest)
   133  			}
   134  		case strings.Contains(p, "/v2/source/referrers/"):
   135  			var referrers []ocispec.Descriptor
   136  			if p == "/v2/source/referrers/"+exampleManifestDescriptor.Digest.String() {
   137  				referrers = []ocispec.Descriptor{exampleSignatureManifestDescriptor}
   138  			}
   139  			result := ocispec.Index{
   140  				Versioned: specs.Versioned{
   141  					SchemaVersion: 2, // historical value. does not pertain to OCI or docker version
   142  				},
   143  				MediaType: ocispec.MediaTypeImageIndex,
   144  				Manifests: referrers,
   145  			}
   146  			if err := json.NewEncoder(w).Encode(result); err != nil {
   147  				panic(err)
   148  			}
   149  		case strings.Contains(p, "/manifests/") && (m == "HEAD" || m == "GET"):
   150  			w.Header().Set("Content-Type", ocispec.MediaTypeImageManifest)
   151  			w.Header().Set("Docker-Content-Digest", string(manifestDesc.Digest))
   152  			w.Header().Set("Content-Length", strconv.Itoa(len([]byte(manifestBlob))))
   153  			w.Write([]byte(manifestBlob))
   154  		case strings.Contains(p, "/blobs/") && (m == "GET" || m == "HEAD"):
   155  			arr := strings.Split(p, "/")
   156  			digest := arr[len(arr)-1]
   157  			var desc ocispec.Descriptor
   158  			var content []byte
   159  			switch digest {
   160  			case layerDesc.Digest.String():
   161  				desc = layerDesc
   162  				content = layerBlob
   163  			case configDesc.Digest.String():
   164  				desc = configDesc
   165  				content = configBlob
   166  			case manifestDesc.Digest.String():
   167  				desc = manifestDesc
   168  				content = manifestBlob
   169  			}
   170  			w.Header().Set("Content-Type", desc.MediaType)
   171  			w.Header().Set("Docker-Content-Digest", digest)
   172  			w.Header().Set("Content-Length", strconv.Itoa(len([]byte(content))))
   173  			w.Write([]byte(content))
   174  		case strings.Contains(p, "/manifests/") && m == "PUT":
   175  			w.WriteHeader(http.StatusCreated)
   176  		}
   177  
   178  	}))
   179  	defer httpsServer.Close()
   180  	u, err := url.Parse(httpsServer.URL)
   181  	if err != nil {
   182  		panic(err)
   183  	}
   184  	remoteHost = u.Host
   185  	http.DefaultTransport = httpsServer.Client().Transport
   186  
   187  	os.Exit(m.Run())
   188  }
   189  
   190  func ExampleCopy_remoteToRemote() {
   191  	reg, err := remote.NewRegistry(remoteHost)
   192  	if err != nil {
   193  		panic(err) // Handle error
   194  	}
   195  	ctx := context.Background()
   196  	src, err := reg.Repository(ctx, "source")
   197  	if err != nil {
   198  		panic(err) // Handle error
   199  	}
   200  	dst, err := reg.Repository(ctx, "target")
   201  	if err != nil {
   202  		panic(err) // Handle error
   203  	}
   204  
   205  	tagName := "latest"
   206  	desc, err := oras.Copy(ctx, src, tagName, dst, tagName, oras.DefaultCopyOptions)
   207  	if err != nil {
   208  		panic(err) // Handle error
   209  	}
   210  	fmt.Println(desc.Digest)
   211  
   212  	// Output:
   213  	// sha256:7cbb44b44e8ede5a89cf193db3f5f2fd019d89697e6b87e8ed2589e60649b0d1
   214  }
   215  
   216  func ExampleCopy_remoteToLocal() {
   217  	reg, err := remote.NewRegistry(remoteHost)
   218  	if err != nil {
   219  		panic(err) // Handle error
   220  	}
   221  
   222  	ctx := context.Background()
   223  	src, err := reg.Repository(ctx, "source")
   224  	if err != nil {
   225  		panic(err) // Handle error
   226  	}
   227  	dst := memory.New()
   228  
   229  	tagName := "latest"
   230  	desc, err := oras.Copy(ctx, src, tagName, dst, tagName, oras.DefaultCopyOptions)
   231  	if err != nil {
   232  		panic(err) // Handle error
   233  	}
   234  	fmt.Println(desc.Digest)
   235  
   236  	// Output:
   237  	// sha256:7cbb44b44e8ede5a89cf193db3f5f2fd019d89697e6b87e8ed2589e60649b0d1
   238  }
   239  
   240  func ExampleCopy_localToLocal() {
   241  	src := exampleMemoryStore
   242  	dst := memory.New()
   243  
   244  	tagName := "latest"
   245  	ctx := context.Background()
   246  	desc, err := oras.Copy(ctx, src, tagName, dst, tagName, oras.DefaultCopyOptions)
   247  	if err != nil {
   248  		panic(err) // Handle error
   249  	}
   250  	fmt.Println(desc.Digest)
   251  
   252  	// Output:
   253  	// sha256:7cbb44b44e8ede5a89cf193db3f5f2fd019d89697e6b87e8ed2589e60649b0d1
   254  }
   255  
   256  func ExampleCopy_localToOciFile() {
   257  	src := exampleMemoryStore
   258  	tempDir, err := os.MkdirTemp("", "oras_oci_example_*")
   259  	if err != nil {
   260  		panic(err) // Handle error
   261  	}
   262  	defer os.RemoveAll(tempDir)
   263  	dst, err := oci.New(tempDir)
   264  	if err != nil {
   265  		panic(err) // Handle error
   266  	}
   267  
   268  	tagName := "latest"
   269  	ctx := context.Background()
   270  	desc, err := oras.Copy(ctx, src, tagName, dst, tagName, oras.DefaultCopyOptions)
   271  	if err != nil {
   272  		panic(err) // Handle error
   273  	}
   274  	fmt.Println(desc.Digest)
   275  
   276  	// Output:
   277  	// sha256:7cbb44b44e8ede5a89cf193db3f5f2fd019d89697e6b87e8ed2589e60649b0d1
   278  }
   279  
   280  func ExampleCopy_localToRemote() {
   281  	src := exampleMemoryStore
   282  	reg, err := remote.NewRegistry(remoteHost)
   283  	if err != nil {
   284  		panic(err) // Handle error
   285  	}
   286  	ctx := context.Background()
   287  	dst, err := reg.Repository(ctx, "target")
   288  	if err != nil {
   289  		panic(err) // Handle error
   290  	}
   291  
   292  	tagName := "latest"
   293  	desc, err := oras.Copy(ctx, src, tagName, dst, tagName, oras.DefaultCopyOptions)
   294  	if err != nil {
   295  		panic(err) // Handle error
   296  	}
   297  	fmt.Println(desc.Digest)
   298  
   299  	// Output:
   300  	// sha256:7cbb44b44e8ede5a89cf193db3f5f2fd019d89697e6b87e8ed2589e60649b0d1
   301  }
   302  
   303  // Example_copyArtifactManifestRemoteToLocal gives an example of copying
   304  // an artifact manifest from a remote repository to local.
   305  func Example_copyArtifactManifestRemoteToLocal() {
   306  	src, err := remote.NewRepository(fmt.Sprintf("%s/source", remoteHost))
   307  	if err != nil {
   308  		panic(err)
   309  	}
   310  	dst := memory.New()
   311  	ctx := context.Background()
   312  
   313  	exampleDigest := "sha256:70c29a81e235dda5c2cebb8ec06eafd3cca346cbd91f15ac74cefd98681c5b3d"
   314  	descriptor, err := src.Resolve(ctx, exampleDigest)
   315  	if err != nil {
   316  		panic(err)
   317  	}
   318  	err = oras.CopyGraph(ctx, src, dst, descriptor, oras.DefaultCopyGraphOptions)
   319  	if err != nil {
   320  		panic(err)
   321  	}
   322  
   323  	// verify that the artifact manifest described by the descriptor exists in dst
   324  	contentExists, err := dst.Exists(ctx, descriptor)
   325  	if err != nil {
   326  		panic(err)
   327  	}
   328  	fmt.Println(contentExists)
   329  
   330  	// Output:
   331  	// true
   332  }
   333  
   334  // Example_extendedCopyArtifactAndReferrersRemoteToLocal gives an example of
   335  // copying an artifact along with its referrers from a remote repository to local.
   336  func Example_extendedCopyArtifactAndReferrersRemoteToLocal() {
   337  	src, err := remote.NewRepository(fmt.Sprintf("%s/source", remoteHost))
   338  	if err != nil {
   339  		panic(err)
   340  	}
   341  	dst := memory.New()
   342  	ctx := context.Background()
   343  
   344  	tagName := "latest"
   345  	// ExtendedCopy will copy the artifact tagged by "latest" along with all of its
   346  	// referrers from src to dst.
   347  	desc, err := oras.ExtendedCopy(ctx, src, tagName, dst, tagName, oras.DefaultExtendedCopyOptions)
   348  	if err != nil {
   349  		panic(err)
   350  	}
   351  
   352  	fmt.Println(desc.Digest)
   353  	// Output:
   354  	// sha256:f396bc4d300934a39ca28ab0d5ac8a3573336d7d63c654d783a68cd1e2057662
   355  }