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 }