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 }