Skip to content

Commit

Permalink
Implement multi-platform support
Browse files Browse the repository at this point in the history
Get logs back

Add index coverage

Add flag

fix test

fix determinism tests

go mod vendor
  • Loading branch information
jonjohnsonjr committed Dec 6, 2019
1 parent 835dcfb commit 532aa42
Show file tree
Hide file tree
Showing 45 changed files with 1,723 additions and 576 deletions.
10 changes: 9 additions & 1 deletion cmd/ko/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,20 @@
package main

import (
"github.com/google/ko/pkg/commands"
"log"
"os"

"github.com/google/go-containerregistry/pkg/logs"
"github.com/google/ko/pkg/commands"

"github.com/spf13/cobra"
)

func init() {
logs.Progress.SetOutput(os.Stderr)
logs.Warn.SetOutput(os.Stderr)
}

func main() {
// Parent command to which all subcommands are added.
cmds := &cobra.Command{
Expand Down
14 changes: 12 additions & 2 deletions pkg/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"context"

v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/types"
)

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

// Build turns the given importpath reference into a v1.Image containing the Go binary.
Build(context.Context, string) (v1.Image, error)
// Build turns the given importpath reference into a v1.Image containing the Go binary
// (or a set of images as a v1.ImageIndex).
Build(context.Context, string) (Result, error)
}

// Result represents the product of a Build. This is usually a v1.Image or v1.ImageIndex.
type Result interface {
MediaType() (types.MediaType, error)
Size() (int64, error)
Digest() (v1.Hash, error)
RawManifest() ([]byte, error)
}
8 changes: 3 additions & 5 deletions pkg/build/future.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@ package build

import (
"sync"

v1 "github.com/google/go-containerregistry/pkg/v1"
)

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

type result struct {
img v1.Image
img Result
err error
}

Expand All @@ -52,7 +50,7 @@ type future struct {
}

// Get blocks on the result of the future.
func (f *future) Get() (v1.Image, error) {
func (f *future) Get() (Result, error) {
// Block on the promise of a result until we get one.
result, ok := <-f.promise
if ok {
Expand Down
7 changes: 3 additions & 4 deletions pkg/build/future_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,14 @@ package build
import (
"testing"

v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/random"
)

func makeImage() (v1.Image, error) {
return random.Image(256, 8)
func makeImage() (Result, error) {
return random.Index(256, 8, 1)
}

func digest(t *testing.T, img v1.Image) string {
func digest(t *testing.T, img Result) string {
d, err := img.Digest()
if err != nil {
t.Fatalf("Digest() = %v", err)
Expand Down
78 changes: 69 additions & 9 deletions pkg/build/gobuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
gb "go/build"
"io"
"io/ioutil"
Expand All @@ -32,17 +33,19 @@ import (
"strings"

v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/mutate"
"github.com/google/go-containerregistry/pkg/v1/tarball"
"github.com/google/go-containerregistry/pkg/v1/types"
)

const (
appDir = "/ko-app"
defaultAppFilename = "ko-app"
)

// GetBase takes an importpath and returns a base v1.Image.
type GetBase func(string) (v1.Image, error)
// GetBase takes an importpath and returns a base image.
type GetBase func(string) (Result, error)
type builder func(context.Context, string, v1.Platform, bool) (string, error)

type gobuild struct {
Expand Down Expand Up @@ -368,12 +371,7 @@ func (g *gobuild) tarKoData(importpath string) (*bytes.Buffer, error) {
}

// Build implements build.Interface
func (gb *gobuild) Build(ctx context.Context, s string) (v1.Image, error) {
// Determine the appropriate base image for this import path.
base, err := gb.getBase(s)
if err != nil {
return nil, err
}
func (gb *gobuild) buildOne(ctx context.Context, s string, base v1.Image) (v1.Image, error) {
cf, err := base.ConfigFile()
if err != nil {
return nil, err
Expand All @@ -384,7 +382,7 @@ func (gb *gobuild) Build(ctx context.Context, s string) (v1.Image, error) {
}

// Do the build into a temporary file.
file, err := gb.build(ctx, s, platform, gb.disableOptimizations)
file, err := build(ctx, s, platform, gb.disableOptimizations)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -464,3 +462,65 @@ func (gb *gobuild) Build(ctx context.Context, s string) (v1.Image, error) {
}
return image, nil
}

// Build implements build.Interface
func (gb *gobuild) Build(ctx context.Context, s string) (Result, error) {
// Determine the appropriate base image for this import path.
base, err := gb.getBase(s)
if err != nil {
return nil, err
}

mt, err := base.MediaType()
if err != nil {
return nil, err
}

switch mt {
case types.OCIImageIndex, types.DockerManifestList:
base, ok := base.(v1.ImageIndex)
if !ok {
return nil, fmt.Errorf("failed to interpret base as index: %v", base)
}
return gb.buildAll(ctx, s, base)
case types.OCIManifestSchema1, types.DockerManifestSchema2:
base, ok := base.(v1.Image)
if !ok {
return nil, fmt.Errorf("failed to interpret base as image: %v", base)
}
return gb.buildOne(ctx, s, base)
default:
return nil, fmt.Errorf("base image media type: %s", mt)
}
}

func (gb *gobuild) buildAll(ctx context.Context, s string, base v1.ImageIndex) (v1.ImageIndex, error) {
im, err := base.IndexManifest()
if err != nil {
return nil, err
}

adds := []mutate.IndexAddendum{}
for _, desc := range im.Manifests {
// This will fail if it's not an image, which is fine for now.
base, err := base.Image(desc.Digest)
if err != nil {
return nil, err
}
img, err := gb.buildOne(ctx, s, base)
if err != nil {
return nil, err
}
adds = append(adds, mutate.IndexAddendum{
Add: img,
Descriptor: v1.Descriptor{
URLs: desc.URLs,
MediaType: desc.MediaType,
Annotations: desc.Annotations,
Platform: desc.Platform,
},
})
}

return mutate.AppendManifests(empty.Index, adds...), nil
}
Loading

0 comments on commit 532aa42

Please sign in to comment.