Skip to content

Commit 03a8e82

Browse files
committed
Implement multi-platform support
1 parent 618b163 commit 03a8e82

27 files changed

+498
-304
lines changed

pkg/build/build.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"context"
1919

2020
v1 "github.com/google/go-containerregistry/pkg/v1"
21+
"github.com/google/go-containerregistry/pkg/v1/types"
2122
)
2223

2324
// Interface abstracts different methods for turning a supported importpath
@@ -28,6 +29,15 @@ type Interface interface {
2829
// TODO(mattmoor): Verify that some base repo: foo.io/bar can be suffixed with this reference and parsed.
2930
IsSupportedReference(string) bool
3031

31-
// Build turns the given importpath reference into a v1.Image containing the Go binary.
32-
Build(context.Context, string) (v1.Image, error)
32+
// Build turns the given importpath reference into a v1.Image containing the Go binary
33+
// (or a set of images as a v1.ImageIndex).
34+
Build(context.Context, string) (Result, error)
35+
}
36+
37+
// Result represents the product of a Build. This is usually a v1.Image or v1.ImageIndex.
38+
type Result interface {
39+
MediaType() (types.MediaType, error)
40+
Size() (int64, error)
41+
Digest() (v1.Hash, error)
42+
RawManifest() ([]byte, error)
3343
}

pkg/build/future.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,9 @@ package build
1616

1717
import (
1818
"sync"
19-
20-
v1 "github.com/google/go-containerregistry/pkg/v1"
2119
)
2220

23-
func newFuture(work func() (v1.Image, error)) *future {
21+
func newFuture(work func() (Result, error)) *future {
2422
// Create a channel on which to send the result.
2523
ch := make(chan *result)
2624
// Initiate the actual work, sending its result
@@ -40,7 +38,7 @@ func newFuture(work func() (v1.Image, error)) *future {
4038
}
4139

4240
type result struct {
43-
img v1.Image
41+
img Result
4442
err error
4543
}
4644

@@ -52,7 +50,7 @@ type future struct {
5250
}
5351

5452
// Get blocks on the result of the future.
55-
func (f *future) Get() (v1.Image, error) {
53+
func (f *future) Get() (Result, error) {
5654
// Block on the promise of a result until we get one.
5755
result, ok := <-f.promise
5856
if ok {

pkg/build/future_test.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,14 @@ package build
1717
import (
1818
"testing"
1919

20-
v1 "github.com/google/go-containerregistry/pkg/v1"
2120
"github.com/google/go-containerregistry/pkg/v1/random"
2221
)
2322

24-
func makeImage() (v1.Image, error) {
25-
return random.Image(256, 8)
23+
func makeImage() (Result, error) {
24+
return random.Index(256, 8, 1)
2625
}
2726

28-
func digest(t *testing.T, img v1.Image) string {
27+
func digest(t *testing.T, img Result) string {
2928
d, err := img.Digest()
3029
if err != nil {
3130
t.Fatalf("Digest() = %v", err)

pkg/build/gobuild.go

Lines changed: 75 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,20 @@ import (
3333
"strings"
3434

3535
v1 "github.com/google/go-containerregistry/pkg/v1"
36+
"github.com/google/go-containerregistry/pkg/v1/empty"
3637
"github.com/google/go-containerregistry/pkg/v1/mutate"
3738
"github.com/google/go-containerregistry/pkg/v1/tarball"
39+
"github.com/google/go-containerregistry/pkg/v1/types"
3840
)
3941

4042
const (
4143
appDir = "/ko-app"
4244
defaultAppFilename = "ko-app"
4345
)
4446

45-
// GetBase takes an importpath and returns a base v1.Image.
46-
type GetBase func(string) (v1.Image, error)
47+
// GetBase takes an importpath and returns a base image.
48+
type GetBase func(string) (Result, error)
49+
4750
type builder func(context.Context, string, v1.Platform, bool) (string, error)
4851

4952
type buildContext interface {
@@ -442,15 +445,9 @@ func (g *gobuild) tarKoData(ref reference) (*bytes.Buffer, error) {
442445
return buf, walkRecursive(tw, root, kodataRoot)
443446
}
444447

445-
// Build implements build.Interface
446-
func (gb *gobuild) Build(ctx context.Context, s string) (v1.Image, error) {
448+
func (gb *gobuild) buildOne(ctx context.Context, s string, base v1.Image) (v1.Image, error) {
447449
ref := newRef(s)
448450

449-
// Determine the appropriate base image for this import path.
450-
base, err := gb.getBase(ref.Path())
451-
if err != nil {
452-
return nil, err
453-
}
454451
cf, err := base.ConfigFile()
455452
if err != nil {
456453
return nil, err
@@ -563,3 +560,72 @@ func updatePath(cf *v1.ConfigFile) {
563560
// If we get here, we never saw PATH.
564561
cf.Config.Env = append(cf.Config.Env, "PATH="+appDir)
565562
}
563+
564+
// Build implements build.Interface
565+
func (gb *gobuild) Build(ctx context.Context, s string) (Result, error) {
566+
// Determine the appropriate base image for this import path.
567+
base, err := gb.getBase(s)
568+
if err != nil {
569+
return nil, err
570+
}
571+
572+
// Determine what kind of base we have and if we should publish an image or an index.
573+
mt, err := base.MediaType()
574+
if err != nil {
575+
return nil, err
576+
}
577+
578+
switch mt {
579+
case types.OCIImageIndex, types.DockerManifestList:
580+
base, ok := base.(v1.ImageIndex)
581+
if !ok {
582+
return nil, fmt.Errorf("failed to interpret base as index: %v", base)
583+
}
584+
return gb.buildAll(ctx, s, base)
585+
case types.OCIManifestSchema1, types.DockerManifestSchema2:
586+
base, ok := base.(v1.Image)
587+
if !ok {
588+
return nil, fmt.Errorf("failed to interpret base as image: %v", base)
589+
}
590+
return gb.buildOne(ctx, s, base)
591+
default:
592+
return nil, fmt.Errorf("base image media type: %s", mt)
593+
}
594+
}
595+
596+
func (gb *gobuild) buildAll(ctx context.Context, s string, base v1.ImageIndex) (v1.ImageIndex, error) {
597+
im, err := base.IndexManifest()
598+
if err != nil {
599+
return nil, err
600+
}
601+
602+
// Build an image for each child from the base and append it to a new index to produce the result.
603+
adds := []mutate.IndexAddendum{}
604+
for _, desc := range im.Manifests {
605+
// TODO(jonjohnsonjr): This would fail for a nested index, which is exceedingly rare.
606+
base, err := base.Image(desc.Digest)
607+
if err != nil {
608+
return nil, err
609+
}
610+
img, err := gb.buildOne(ctx, s, base)
611+
if err != nil {
612+
return nil, err
613+
}
614+
adds = append(adds, mutate.IndexAddendum{
615+
Add: img,
616+
Descriptor: v1.Descriptor{
617+
URLs: desc.URLs,
618+
MediaType: desc.MediaType,
619+
Annotations: desc.Annotations,
620+
Platform: desc.Platform,
621+
},
622+
})
623+
}
624+
625+
baseType, err := base.MediaType()
626+
if err != nil {
627+
return nil, err
628+
}
629+
630+
return mutate.IndexMediaType(mutate.AppendManifests(empty.Index, adds...), baseType), nil
631+
}

0 commit comments

Comments
 (0)