Skip to content

Commit 17e7d15

Browse files
docs: add quick start for oras-go (#939)
Fixes #816 Signed-off-by: Xiaoxuan Wang <[email protected]>
1 parent 34a87ef commit 17e7d15

File tree

1 file changed

+367
-0
lines changed

1 file changed

+367
-0
lines changed

docs/tutorial/quickstart.md

Lines changed: 367 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,367 @@
1+
# Tutorial: Get started with oras-go v2
2+
3+
This tutorial introduces the basics of managing OCI artifacts with the [oras-go v2](https://pkg.go.dev/oras.land/oras-go/v2) package.
4+
5+
You'll get the most out of this tutorial if you have a basic familiarity with Go and its tooling. If this is your first exposure to Go, please see [Tutorial: Get started with Go](https://golang.org/doc/tutorial/getting-started) for a quick introduction.
6+
7+
The tutorial includes the following sections:
8+
9+
1. Create a folder for your code.
10+
2. Connect to a remote repository.
11+
3. Show tags in the repository.
12+
4. Push a layer to the repository.
13+
5. Push a manifest to the repository.
14+
6. Fetch the manifest from the repository.
15+
7. Parse the fetched manifest content and get the layers.
16+
8. Copy the artifact from the repository.
17+
18+
The complete code is provided at the end of this tutorial.
19+
20+
## Create a folder for your code
21+
22+
Open a command prompt and cd to your working directory.
23+
24+
Create a directory for your code.
25+
```shell
26+
mkdir oras-go-v2-quickstart
27+
cd oras-go-v2-quickstart
28+
```
29+
Create a module in which you can manage dependencies.
30+
31+
```console
32+
$ go mod init quickstart/oras-go-v2
33+
go: creating new go.mod: quickstart/oras-go-v2
34+
```
35+
36+
Import the `oras-go v2` package.
37+
```console
38+
$ go get oras.land/oras-go/v2
39+
go: added github.com/opencontainers/go-digest v1.0.0
40+
go: added github.com/opencontainers/image-spec v1.1.0
41+
go: added golang.org/x/sync v0.6.0
42+
go: added oras.land/oras-go/v2 v2.5.0
43+
```
44+
45+
In your text editor, create a file `main.go` in which to write your code.
46+
47+
## Connect to a remote repository with token authentication
48+
49+
Paste the following into `main.go` and save the file. This code demonstrates how to use [NewRepository](https://pkg.go.dev/oras.land/oras-go/v2/registry/remote#NewRepository) from the [registry/remote](https://pkg.go.dev/oras.land/oras-go/v2/registry/remote) package to connect to a remote repository. Token authentication (using a username and password, see reference [here](https://distribution.github.io/distribution/spec/auth/token/)) is handled by the [registry/remote/auth](https://pkg.go.dev/oras.land/oras-go/v2/registry/remote/auth) package. Other authentication methods, such as refresh token authentication, are also supported.
50+
51+
```go
52+
package main
53+
54+
import (
55+
"oras.land/oras-go/v2/registry/remote"
56+
"oras.land/oras-go/v2/registry/remote/auth"
57+
"oras.land/oras-go/v2/registry/remote/retry"
58+
)
59+
60+
func main() {
61+
// 1. Connect to a remote repository with token authentication
62+
ref := "example.registry.com/myrepo"
63+
repo, err := remote.NewRepository(ref)
64+
if err != nil {
65+
panic(err)
66+
}
67+
// Note: The below code can be omitted if authentication is not required.
68+
repo.Client = &auth.Client{
69+
Client: retry.DefaultClient,
70+
Cache: auth.NewCache(),
71+
Credential: auth.StaticCredential(repo.Reference.Registry, auth.Credential{
72+
Username: "username",
73+
Password: "password",
74+
}),
75+
}
76+
}
77+
```
78+
79+
## Show tags in the repository
80+
Add these two lines to the `import` block of `main.go`.
81+
82+
```go
83+
"context"
84+
"fmt"
85+
```
86+
87+
The following code snippet uses the [(*Repository) Tags](https://pkg.go.dev/oras.land/oras-go/v2/registry/remote#Repository.Tags) method to list the tags in the repository. Paste the code into `main.go` after the last section.
88+
89+
```go
90+
// 2. Show the tags in the repository
91+
ctx := context.Background()
92+
err = repo.Tags(ctx, "", func(tags []string) error {
93+
for _, tag := range tags {
94+
fmt.Println(tag)
95+
}
96+
return nil
97+
})
98+
if err != nil {
99+
panic(err)
100+
}
101+
```
102+
103+
### Run the code
104+
105+
Run `go mod tidy` to clean up dependencies.
106+
107+
```shell
108+
go mod tidy
109+
```
110+
111+
Run the code.
112+
```shell
113+
go run .
114+
```
115+
116+
You should see the tags in the repository.
117+
118+
## Push a layer to the repository
119+
120+
All referenced layers must exist in the repository before a manifest can be pushed, so we need to push manifest layers before we can push a manifest.
121+
122+
Add these two lines to the `import` block of `main.go`.
123+
124+
```go
125+
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
126+
"oras.land/oras-go/v2"
127+
```
128+
129+
The following code snippet demonstrates how to push a manifest layer with [PushBytes](https://pkg.go.dev/oras.land/oras-go/v2#PushBytes). Paste the code into `main.go` after the last section.
130+
131+
```go
132+
// 3. push a layer to the repository
133+
layer := []byte("example manifest layer")
134+
layerDescriptor, err := oras.PushBytes(ctx, repo, ocispec.MediaTypeImageLayer, layer)
135+
if err != nil {
136+
panic(err)
137+
}
138+
fmt.Println("Pushed manifest layer:", layerDescriptor.Digest)
139+
```
140+
141+
## Push a manifest to the repository with the tag "quickstart"
142+
143+
The following code snippet demonstrates how to pack a manifest and push it to the repository with the tag "quickstart" using the [PackManifest](https://pkg.go.dev/oras.land/oras-go/v2#PackManifest) and the [(*Repository) Tag](https://pkg.go.dev/oras.land/oras-go/v2/registry/remote#Repository.Tag) methods. Paste the code into `main.go` after the last section.
144+
145+
```go
146+
// 4. Push a manifest to the repository with the tag "quickstart"
147+
packOpts := oras.PackManifestOptions{
148+
Layers: []ocispec.Descriptor{layerDescriptor},
149+
}
150+
artifactType := "application/vnd.example+type"
151+
desc, err := oras.PackManifest(ctx, repo, oras.PackManifestVersion1_1, artifactType, packOpts)
152+
if err != nil {
153+
panic(err)
154+
}
155+
tag := "quickstart"
156+
err = repo.Tag(ctx, desc, tag)
157+
if err != nil {
158+
panic(err)
159+
}
160+
fmt.Println("Pushed and tagged manifest")
161+
```
162+
163+
## Fetch the manifest from the repository by tag
164+
165+
The following code snippet demonstrates how to fetch a manifest from the repository by its tag with [FetchBytes](https://pkg.go.dev/oras.land/oras-go/v2#FetchBytes). Paste the code into `main.go` after the last section.
166+
167+
```go
168+
// 5. Fetch the manifest from the repository by tag
169+
_, fetchedManifestContent, err := oras.FetchBytes(ctx, repo, tag, oras.DefaultFetchBytesOptions)
170+
if err != nil {
171+
panic(err)
172+
}
173+
fmt.Println(string(fetchedManifestContent))
174+
```
175+
176+
## Parse the fetched manifest content and get the layers
177+
178+
Add these two lines to the `import` block of `main.go`.
179+
```go
180+
"encoding/json"
181+
"oras.land/oras-go/v2/content"
182+
```
183+
184+
The following code snippet demonstrates how to parse the fetched manifest content and get the layers. [FetchAll](https://pkg.go.dev/oras.land/oras-go/v2/content#FetchAll) from the [content](https://pkg.go.dev/oras.land/oras-go/v2/content) package is used to read and fetch the content identified by a descriptor. Paste the code into `main.go` after the last section.
185+
186+
```go
187+
// 6. Parse the fetched manifest content and get the layers
188+
var manifest ocispec.Manifest
189+
if err := json.Unmarshal(fetchedManifestContent, &manifest); err != nil {
190+
panic(err)
191+
}
192+
for _, layer := range manifest.Layers {
193+
layerContent, err := content.FetchAll(ctx, repo, layer)
194+
if err != nil {
195+
panic(err)
196+
}
197+
fmt.Println(string(layerContent))
198+
}
199+
```
200+
201+
## Copy the artifact to local OCI layout directory from the repository
202+
203+
Add these two lines to the `import` block of `main.go`.
204+
```go
205+
"os"
206+
"oras.land/oras-go/v2/content/oci"
207+
```
208+
209+
The following code snippet demonstrates how to copy an artifact from the repository by its tag and save it to the current directory in the [OCI layout](https://github.com/opencontainers/image-spec/blob/main/image-layout.md) format. The copy operation is performed using [Copy](https://pkg.go.dev/oras.land/oras-go/v2#Copy). Paste the code into `main.go` after the last section.
210+
211+
```go
212+
// 7. Copy the artifact to local OCI layout directory with a tag "quickstartOCI"
213+
ociDir, err := os.MkdirTemp(".", "oras_oci_example_*")
214+
if err != nil {
215+
panic(err)
216+
}
217+
ociTarget, err := oci.New(ociDir)
218+
if err != nil {
219+
panic(err)
220+
}
221+
_, err = oras.Copy(ctx, repo, tag, ociTarget, "quickstartOCI", oras.DefaultCopyOptions)
222+
if err != nil {
223+
panic(err)
224+
}
225+
fmt.Println("Copied the artifact")
226+
```
227+
228+
## Run the code
229+
230+
Run the code.
231+
```shell
232+
go run .
233+
```
234+
235+
You should see a similar output on the terminal as below and an OCI layout folder in the current directory.
236+
```
237+
tag1
238+
tag2
239+
tag3
240+
Pushed manifest layer: sha256:4f19474743ecb04b60156ea41b73e06fdf6a5b758e007b788aaa92595dcd3a49
241+
Pushed and tagged manifest
242+
{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","artifactType":"application/vnd.example+type","config":{"mediaType":"application/vnd.oci.empty.v1+json","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2,"data":"e30="},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar","digest":"sha256:4f19474743ecb04b60156ea41b73e06fdf6a5b758e007b788aaa92595dcd3a49","size":22}],"annotations":{"org.opencontainers.image.created":"2025-04-18T03:15:26Z"}}
243+
example manifest layer
244+
Copied the artifact
245+
```
246+
247+
## Conclusion
248+
249+
Congratulations! You’ve completed this tutorial.
250+
251+
Suggested next steps:
252+
* Check out more [examples](https://pkg.go.dev/oras.land/oras-go/v2#pkg-overview) in the documentation.
253+
* Learn about how `oras-go` v2 [models artifacts](https://github.com/oras-project/oras-go/blob/main/docs/Modeling-Artifacts.md).
254+
* Learn about [Targets and Content Stores](https://github.com/oras-project/oras-go/blob/main/docs/Targets.md) in `oras-go` v2.
255+
256+
## Completed Code
257+
258+
This section contains the completed code from this tutorial.
259+
260+
```go
261+
package main
262+
263+
import (
264+
"context"
265+
"encoding/json"
266+
"fmt"
267+
"os"
268+
269+
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
270+
"oras.land/oras-go/v2"
271+
"oras.land/oras-go/v2/content"
272+
"oras.land/oras-go/v2/content/oci"
273+
"oras.land/oras-go/v2/registry"
274+
"oras.land/oras-go/v2/registry/remote"
275+
"oras.land/oras-go/v2/registry/remote/auth"
276+
"oras.land/oras-go/v2/registry/remote/retry"
277+
)
278+
279+
func main() {
280+
// 1. Connect to a remote repository with token authentication
281+
ref := "example.registry.com/myrepo"
282+
repo, err := remote.NewRepository(ref)
283+
if err != nil {
284+
panic(err)
285+
}
286+
// Note: The below code can be omitted if authentication is not required.
287+
repo.Client = &auth.Client{
288+
Client: retry.DefaultClient,
289+
Cache: auth.NewCache(),
290+
Credential: auth.StaticCredential(repo.Reference.Registry, auth.Credential{
291+
Username: "username",
292+
Password: "password",
293+
}),
294+
}
295+
296+
// 2. Show the tags in the repository
297+
ctx := context.Background()
298+
err = repo.Tags(ctx, "", func(tags []string) error {
299+
for _, tag := range tags {
300+
fmt.Println(tag)
301+
}
302+
return nil
303+
})
304+
if err != nil {
305+
panic(err)
306+
}
307+
308+
// 3. push a layer to the repository
309+
layer := []byte("example manifest layer")
310+
layerDescriptor, err := oras.PushBytes(ctx, repo, ocispec.MediaTypeImageLayer, layer)
311+
if err != nil {
312+
panic(err)
313+
}
314+
fmt.Println("Pushed manifest layer:", layerDescriptor.Digest)
315+
316+
// 4. Push a manifest to the repository with the tag "quickstart"
317+
packOpts := oras.PackManifestOptions{
318+
Layers: []ocispec.Descriptor{layerDescriptor},
319+
}
320+
artifactType := "application/vnd.example+type"
321+
desc, err := oras.PackManifest(ctx, repo, oras.PackManifestVersion1_1, artifactType, packOpts)
322+
if err != nil {
323+
panic(err)
324+
}
325+
tag := "quickstart"
326+
err = repo.Tag(ctx, desc, tag)
327+
if err != nil {
328+
panic(err)
329+
}
330+
fmt.Println("Pushed and tagged manifest")
331+
332+
// 5. Fetch the manifest from the repository by tag
333+
_, fetchedManifestContent, err := oras.FetchBytes(ctx, repo, tag, oras.DefaultFetchBytesOptions)
334+
if err != nil {
335+
panic(err)
336+
}
337+
fmt.Println(string(fetchedManifestContent))
338+
339+
// 6. Parse the fetched manifest content and get the layers
340+
var manifest ocispec.Manifest
341+
if err := json.Unmarshal(fetchedManifestContent, &manifest); err != nil {
342+
panic(err)
343+
}
344+
for _, layer := range manifest.Layers {
345+
layerContent, err := content.FetchAll(ctx, repo, layer)
346+
if err != nil {
347+
panic(err)
348+
}
349+
fmt.Println(string(layerContent))
350+
}
351+
352+
// 7. Copy the artifact to local OCI layout directory with a tag "quickstartOCI"
353+
ociDir, err := os.MkdirTemp(".", "oras_oci_example_*")
354+
if err != nil {
355+
panic(err)
356+
}
357+
ociTarget, err := oci.New(ociDir)
358+
if err != nil {
359+
panic(err)
360+
}
361+
_, err = oras.Copy(ctx, repo, tag, ociTarget, "quickstartOCI", oras.DefaultCopyOptions)
362+
if err != nil {
363+
panic(err)
364+
}
365+
fmt.Println("Copied the artifact")
366+
}
367+
```

0 commit comments

Comments
 (0)