@@ -19,6 +19,7 @@ import (
19
19
"encoding/json"
20
20
"fmt"
21
21
"io"
22
+ "runtime"
22
23
23
24
v1 "github.com/opencontainers/image-spec/specs-go/v1"
24
25
"oras.land/oras-go/v2"
@@ -154,3 +155,75 @@ func manifestFromDesc(ctx context.Context, target oras.Target, desc *v1.Descript
154
155
155
156
return & manifest , nil
156
157
}
158
+
159
+ // PullConfig fetches only the config layer from a given ref.
160
+ func (p * Puller ) PullConfigLayer (ctx context.Context , ref string ) (io.Reader , error ) {
161
+ repo , err := repository .NewRepository (ref , repository .WithClient (p .Client ))
162
+ if err != nil {
163
+ return nil , err
164
+ }
165
+
166
+ desc , manifestReader , err := repo .FetchReference (ctx , ref )
167
+ if err != nil {
168
+ return nil , fmt .Errorf ("unable to fetch reference %q" , ref )
169
+ }
170
+ defer manifestReader .Close ()
171
+
172
+ // Resolve to actual manifest if an index is found.
173
+ if desc .MediaType == v1 .MediaTypeImageIndex {
174
+ var index v1.Index
175
+ _ , indexReader , err := repo .FetchReference (ctx , ref )
176
+ if err != nil {
177
+ return nil , fmt .Errorf ("unable to fetch index for ref %q" , ref )
178
+ }
179
+
180
+ indexBytes , err := io .ReadAll (indexReader )
181
+ if err = json .Unmarshal (indexBytes , & index ); err != nil {
182
+ return nil , fmt .Errorf ("unable to unmarshal manifest: %w" , err )
183
+ }
184
+
185
+ // todo: decide if goos or arch should be passed to this function
186
+ found := false
187
+ for _ , manifest := range index .Manifests {
188
+ if manifest .Platform .OS == runtime .GOOS &&
189
+ manifest .Platform .Architecture == runtime .GOARCH {
190
+ desc = manifest
191
+ found = true
192
+ break
193
+ }
194
+ }
195
+
196
+ if ! found {
197
+ return nil , fmt .Errorf ("unable to find a manifest matching the given platform: %s %s" , runtime .GOOS , runtime .GOARCH )
198
+ }
199
+
200
+ manifestReader , err = repo .Fetch (ctx , desc )
201
+ if err != nil {
202
+ return nil , fmt .Errorf ("unable to fetch manifest desc with digest %s: %w" , desc .Digest .String (), err )
203
+ }
204
+ }
205
+
206
+ var manifest v1.Manifest
207
+ manifestBytes , err := io .ReadAll (manifestReader )
208
+ if err != nil {
209
+ return nil , fmt .Errorf ("unable to read bytes from manifest reader for ref %q" , ref )
210
+ }
211
+
212
+ if err = json .Unmarshal (manifestBytes , & manifest ); err != nil {
213
+ return nil , fmt .Errorf ("unable to unmarshal manifest: %w" , err )
214
+ }
215
+
216
+ configRef := manifest .Config .Digest .String ()
217
+
218
+ descriptor , err := repo .Blobs ().Resolve (ctx , configRef )
219
+ if err != nil {
220
+ return nil , fmt .Errorf ("unable to resolve to get descriptor for config blob %q" , configRef )
221
+ }
222
+
223
+ rc , err := repo .Fetch (ctx , descriptor )
224
+ if err != nil {
225
+ return nil , fmt .Errorf ("unable to fetch descriptor with digest: %s" , desc .Digest .String ())
226
+ }
227
+
228
+ return rc , nil
229
+ }
0 commit comments