Skip to content

Commit 026422b

Browse files
committed
Implement multi-platform support
1 parent 618b163 commit 026422b

27 files changed

Lines changed: 493 additions & 302 deletions

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: 73 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,70 @@ 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+
mt, err := base.MediaType()
573+
if err != nil {
574+
return nil, err
575+
}
576+
577+
switch mt {
578+
case types.OCIImageIndex, types.DockerManifestList:
579+
base, ok := base.(v1.ImageIndex)
580+
if !ok {
581+
return nil, fmt.Errorf("failed to interpret base as index: %v", base)
582+
}
583+
return gb.buildAll(ctx, s, base)
584+
case types.OCIManifestSchema1, types.DockerManifestSchema2:
585+
base, ok := base.(v1.Image)
586+
if !ok {
587+
return nil, fmt.Errorf("failed to interpret base as image: %v", base)
588+
}
589+
return gb.buildOne(ctx, s, base)
590+
default:
591+
return nil, fmt.Errorf("base image media type: %s", mt)
592+
}
593+
}
594+
595+
func (gb *gobuild) buildAll(ctx context.Context, s string, base v1.ImageIndex) (v1.ImageIndex, error) {
596+
im, err := base.IndexManifest()
597+
if err != nil {
598+
return nil, err
599+
}
600+
601+
adds := []mutate.IndexAddendum{}
602+
for _, desc := range im.Manifests {
603+
// This will fail if it's not an image, which is fine for now.
604+
base, err := base.Image(desc.Digest)
605+
if err != nil {
606+
return nil, err
607+
}
608+
img, err := gb.buildOne(ctx, s, base)
609+
if err != nil {
610+
return nil, err
611+
}
612+
adds = append(adds, mutate.IndexAddendum{
613+
Add: img,
614+
Descriptor: v1.Descriptor{
615+
URLs: desc.URLs,
616+
MediaType: desc.MediaType,
617+
Annotations: desc.Annotations,
618+
Platform: desc.Platform,
619+
},
620+
})
621+
}
622+
623+
baseType, err := base.MediaType()
624+
if err != nil {
625+
return nil, err
626+
}
627+
628+
return mutate.IndexMediaType(mutate.AppendManifests(empty.Index, adds...), baseType), nil
629+
}

0 commit comments

Comments
 (0)