Skip to content

Commit db82727

Browse files
committed
mkctr: add flag to save image/images to disk
This commit adds a new flag, --out-path which will allow the user to write the image to disk; if building a single image, e.g. with --target local, a tar is created at the out-path. if building a multi arch image, then a directorty is created with the images and oci manifest. Signed-off-by: Kristoffer Dalby <[email protected]>
1 parent 5497735 commit db82727

File tree

1 file changed

+87
-31
lines changed

1 file changed

+87
-31
lines changed

mkctr.go

Lines changed: 87 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"log"
1818
"os"
1919
"os/exec"
20+
"path"
2021
"path/filepath"
2122
"runtime"
2223
"strings"
@@ -27,6 +28,7 @@ import (
2728
v1 "github.com/google/go-containerregistry/pkg/v1"
2829
"github.com/google/go-containerregistry/pkg/v1/daemon"
2930
"github.com/google/go-containerregistry/pkg/v1/empty"
31+
"github.com/google/go-containerregistry/pkg/v1/layout"
3032
"github.com/google/go-containerregistry/pkg/v1/mutate"
3133
"github.com/google/go-containerregistry/pkg/v1/remote"
3234
"github.com/google/go-containerregistry/pkg/v1/tarball"
@@ -79,6 +81,7 @@ type buildParams struct {
7981
staticFiles map[string]string
8082
imageRefs []name.Tag
8183
publish bool
84+
outPath string
8285
ldflags string
8386
gotags string
8487
target string
@@ -96,6 +99,7 @@ func main() {
9699
ldflagsArg = flag.String("ldflags", "", "the --ldflags value to pass to go")
97100
gotags = flag.String("gotags", "", "the --tags value to pass to go")
98101
push = flag.Bool("push", false, "publish the image")
102+
outPath = flag.String("out", "", "writes image(s) to a given folder")
99103
target = flag.String("target", "", "build for a specific env (options: flyio, local)")
100104
verbose = flag.Bool("v", false, "verbose build output")
101105
annotations = flag.String("annotations", "", `OCI image annotations https://github.com/opencontainers/image-spec/blob/main/annotations.md.
@@ -140,6 +144,7 @@ func main() {
140144
staticFiles: staticFiles,
141145
imageRefs: refs,
142146
publish: *push,
147+
outPath: *outPath,
143148
ldflags: *ldflagsArg,
144149
gotags: *gotags,
145150
target: *target,
@@ -198,6 +203,34 @@ func verifyPlatform(p v1.Platform, target string) error {
198203
return nil
199204
}
200205

206+
func createOutDirectory(path string) error {
207+
fi, err := os.Stat(path)
208+
if err != nil {
209+
if !os.IsNotExist(err) {
210+
return fmt.Errorf("checking out path: %w", err)
211+
}
212+
}
213+
if fi != nil && !fi.IsDir() {
214+
return fmt.Errorf("out must be a directory: %s", path)
215+
}
216+
if err = os.MkdirAll(path, 0755); err != nil {
217+
return fmt.Errorf("creating out directory: %w", err)
218+
}
219+
return nil
220+
}
221+
222+
func writeImageToFile(img v1.Image, imgRef name.Reference, p string) error {
223+
err := createOutDirectory(p)
224+
if err != nil {
225+
return err
226+
}
227+
if err := tarball.WriteToFile(path.Join(p, "image.tar"), imgRef, img); err != nil {
228+
return err
229+
}
230+
231+
return nil
232+
}
233+
201234
func fetchAndBuild(bp *buildParams) error {
202235
ctx := context.Background()
203236
logf := log.Printf
@@ -243,25 +276,30 @@ func fetchAndBuild(bp *buildParams) error {
243276
if err != nil {
244277
return err
245278
}
246-
if !bp.publish {
247-
logf("not pushing")
248-
return nil
249-
}
250279

251-
img = mutate.Annotations(img, bp.annotations).(v1.Image) // OCI annotations
280+
switch {
281+
case bp.publish:
282+
img = mutate.Annotations(img, bp.annotations).(v1.Image) // OCI annotations
252283

253-
for _, r := range bp.imageRefs {
254-
if bp.target == "local" {
255-
if err := loadLocalImage(logf, r, img); err != nil {
284+
for _, r := range bp.imageRefs {
285+
if bp.target == "local" {
286+
if err := loadLocalImage(logf, r, img); err != nil {
287+
return err
288+
}
289+
continue
290+
}
291+
logf("pushing to %v", r)
292+
if err := remote.Write(r, img, remoteOpts...); err != nil {
256293
return err
257294
}
258-
continue
259-
}
260-
logf("pushing to %v", r)
261-
if err := remote.Write(r, img, remoteOpts...); err != nil {
262-
return err
263295
}
296+
return nil
297+
298+
case bp.outPath != "":
299+
return writeImageToFile(img, bp.imageRefs[0], bp.outPath)
264300
}
301+
logf("not pushing or writing to file")
302+
265303
return nil
266304
case types.OCIImageIndex, types.DockerManifestList:
267305
// baseRef is a multi-platform index, rest of the method handles this.
@@ -336,23 +374,28 @@ func fetchAndBuild(bp *buildParams) error {
336374
return err
337375
}
338376
logf("image digest: %v", d)
339-
if !bp.publish {
340-
logf("not pushing")
341-
return nil
342-
}
343377

344-
for _, r := range bp.imageRefs {
345-
if bp.target == "local" {
346-
if err := loadLocalImage(logf, r, img); err != nil {
378+
switch {
379+
case bp.publish:
380+
for _, r := range bp.imageRefs {
381+
if bp.target == "local" {
382+
if err := loadLocalImage(logf, r, img); err != nil {
383+
return err
384+
}
385+
continue
386+
}
387+
logf("pushing to %v", r)
388+
if err := remote.Write(r, img, remoteOpts...); err != nil {
347389
return err
348390
}
349-
continue
350-
}
351-
logf("pushing to %v", r)
352-
if err := remote.Write(r, img, remoteOpts...); err != nil {
353-
return err
354391
}
392+
return nil
393+
394+
case bp.outPath != "":
395+
return writeImageToFile(img, bp.imageRefs[0], bp.outPath)
355396
}
397+
logf("not pushing or writing to file")
398+
356399
return nil
357400
}
358401
if bp.target == "local" {
@@ -371,17 +414,30 @@ func fetchAndBuild(bp *buildParams) error {
371414
idx = mutate.Annotations(idx, bp.annotations).(v1.ImageIndex)
372415

373416
logf("index digest: %v", d)
374-
if !bp.publish {
375-
logf("not pushing")
417+
418+
switch {
419+
case bp.publish:
420+
for _, r := range bp.imageRefs {
421+
logf("pushing to %v", r)
422+
if err := remote.WriteIndex(r, idx, remoteOpts...); err != nil {
423+
return err
424+
}
425+
}
426+
376427
return nil
377-
}
378428

379-
for _, r := range bp.imageRefs {
380-
logf("pushing to %v", r)
381-
if err := remote.WriteIndex(r, idx, remoteOpts...); err != nil {
429+
case bp.outPath != "":
430+
err := createOutDirectory(bp.outPath)
431+
if err != nil {
432+
return err
433+
}
434+
if _, err := layout.Write(bp.outPath, idx); err != nil {
382435
return err
383436
}
437+
438+
return nil
384439
}
440+
logf("not pushing or writing to file")
385441

386442
return nil
387443
}

0 commit comments

Comments
 (0)