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 }