1
0
mirror of https://github.com/ko-build/ko.git synced 2024-12-15 09:04:09 +02:00
ko-build/pkg/build/gobuild_test.go
Adam Harwayne b0a1702132 Add --disable-optimizations flag to make debugging easier. (#24)
* Ignore GoLand files.

* Add --disable-optimizations flag to make debugging easier.

* Fix unit test (by ignoring the new functionality).
2019-04-18 10:18:29 -07:00

317 lines
8.4 KiB
Go

// Copyright 2018 Google LLC All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package build
import (
"archive/tar"
"io"
"io/ioutil"
"path/filepath"
"testing"
"time"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/random"
)
func TestGoBuildIsSupportedRef(t *testing.T) {
base, err := random.Image(1024, 3)
if err != nil {
t.Fatalf("random.Image() = %v", err)
}
ng, err := NewGo(WithBaseImages(func(string) (v1.Image, error) { return base, nil }))
if err != nil {
t.Fatalf("NewGo() = %v", err)
}
// Supported import paths.
for _, importpath := range []string{
filepath.FromSlash("github.com/google/ko/cmd/ko"), // ko can build itself.
} {
t.Run(importpath, func(t *testing.T) {
if !ng.IsSupportedReference(importpath) {
t.Errorf("IsSupportedReference(%q) = false, want true", importpath)
}
})
}
// Unsupported import paths.
for _, importpath := range []string{
filepath.FromSlash("github.com/google/ko/pkg/build"), // not a command.
filepath.FromSlash("github.com/google/ko/pkg/nonexistent"), // does not exist.
} {
t.Run(importpath, func(t *testing.T) {
if ng.IsSupportedReference(importpath) {
t.Errorf("IsSupportedReference(%v) = true, want false", importpath)
}
})
}
}
// A helper method we use to substitute for the default "build" method.
func writeTempFile(s string, _ bool) (string, error) {
tmpDir, err := ioutil.TempDir("", "ko")
if err != nil {
return "", err
}
file, err := ioutil.TempFile(tmpDir, "out")
if err != nil {
return "", err
}
defer file.Close()
if _, err := file.WriteString(filepath.ToSlash(s)); err != nil {
return "", err
}
return file.Name(), nil
}
func TestGoBuildNoKoData(t *testing.T) {
baseLayers := int64(3)
base, err := random.Image(1024, baseLayers)
if err != nil {
t.Fatalf("random.Image() = %v", err)
}
importpath := "github.com/google/ko"
creationTime := v1.Time{time.Unix(5000, 0)}
ng, err := NewGo(
WithCreationTime(creationTime),
WithBaseImages(func(string) (v1.Image, error) { return base, nil }),
withBuilder(writeTempFile),
)
if err != nil {
t.Fatalf("NewGo() = %v", err)
}
img, err := ng.Build(filepath.Join(importpath, "cmd", "ko"))
if err != nil {
t.Fatalf("Build() = %v", err)
}
ls, err := img.Layers()
if err != nil {
t.Fatalf("Layers() = %v", err)
}
// Check that we have the expected number of layers.
t.Run("check layer count", func(t *testing.T) {
// We get a layer for the go binary and a layer for the kodata/
if got, want := int64(len(ls)), baseLayers+2; got != want {
t.Fatalf("len(Layers()) = %v, want %v", got, want)
}
})
// Check that rebuilding the image again results in the same image digest.
t.Run("check determinism", func(t *testing.T) {
expectedHash := v1.Hash{
Algorithm: "sha256",
Hex: "fb82c95fc73eaf26d0b18b1bc2d23ee32059e46806a83a313e738aac4d039492",
}
appLayer := ls[baseLayers+1]
if got, err := appLayer.Digest(); err != nil {
t.Errorf("Digest() = %v", err)
} else if got != expectedHash {
t.Errorf("Digest() = %v, want %v", got, expectedHash)
}
})
// Check that the entrypoint of the image is configured to invoke our Go application
t.Run("check entrypoint", func(t *testing.T) {
cfg, err := img.ConfigFile()
if err != nil {
t.Errorf("ConfigFile() = %v", err)
}
entrypoint := cfg.Config.Entrypoint
if got, want := len(entrypoint), 1; got != want {
t.Errorf("len(entrypoint) = %v, want %v", got, want)
}
if got, want := entrypoint[0], "/ko-app/ko"; got != want {
t.Errorf("entrypoint = %v, want %v", got, want)
}
})
t.Run("check creation time", func(t *testing.T) {
cfg, err := img.ConfigFile()
if err != nil {
t.Errorf("ConfigFile() = %v", err)
}
actual := cfg.Created
if actual.Time != creationTime.Time {
t.Errorf("created = %v, want %v", actual, creationTime)
}
})
}
func TestGoBuild(t *testing.T) {
baseLayers := int64(3)
base, err := random.Image(1024, baseLayers)
if err != nil {
t.Fatalf("random.Image() = %v", err)
}
importpath := "github.com/google/ko"
creationTime := v1.Time{time.Unix(5000, 0)}
ng, err := NewGo(
WithCreationTime(creationTime),
WithBaseImages(func(string) (v1.Image, error) { return base, nil }),
withBuilder(writeTempFile),
)
if err != nil {
t.Fatalf("NewGo() = %v", err)
}
img, err := ng.Build(filepath.Join(importpath, "cmd", "ko", "test"))
if err != nil {
t.Fatalf("Build() = %v", err)
}
ls, err := img.Layers()
if err != nil {
t.Fatalf("Layers() = %v", err)
}
// Check that we have the expected number of layers.
t.Run("check layer count", func(t *testing.T) {
// We get a layer for the go binary and a layer for the kodata/
if got, want := int64(len(ls)), baseLayers+2; got != want {
t.Fatalf("len(Layers()) = %v, want %v", got, want)
}
})
// Check that rebuilding the image again results in the same image digest.
t.Run("check determinism", func(t *testing.T) {
expectedHash := v1.Hash{
Algorithm: "sha256",
Hex: "4c7f97dda30576670c3a8967424f7dea023030bb3df74fc4bd10329bcb266fc2",
}
appLayer := ls[baseLayers+1]
if got, err := appLayer.Digest(); err != nil {
t.Errorf("Digest() = %v", err)
} else if got != expectedHash {
t.Errorf("Digest() = %v, want %v", got, expectedHash)
}
})
t.Run("check app layer contents", func(t *testing.T) {
expectedHash := v1.Hash{
Algorithm: "sha256",
Hex: "63b6e090921b79b61e7f5fba44d2ea0f81215d9abac3d005dda7cb9a1f8a025d",
}
appLayer := ls[baseLayers]
if got, err := appLayer.Digest(); err != nil {
t.Errorf("Digest() = %v", err)
} else if got != expectedHash {
t.Errorf("Digest() = %v, want %v", got, expectedHash)
}
r, err := appLayer.Uncompressed()
if err != nil {
t.Errorf("Uncompressed() = %v", err)
}
defer r.Close()
tr := tar.NewReader(r)
if _, err := tr.Next(); err == io.EOF {
t.Errorf("Layer contained no files")
}
})
// Check that the kodata layer contains the expected data (even though it was a symlink
// outside kodata).
t.Run("check kodata", func(t *testing.T) {
dataLayer := ls[baseLayers]
r, err := dataLayer.Uncompressed()
if err != nil {
t.Errorf("Uncompressed() = %v", err)
}
defer r.Close()
found := false
tr := tar.NewReader(r)
for {
header, err := tr.Next()
if err == io.EOF {
break
} else if err != nil {
t.Errorf("Next() = %v", err)
continue
}
if header.Name != filepath.Join(kodataRoot, "kenobi") {
continue
}
found = true
body, err := ioutil.ReadAll(tr)
if err != nil {
t.Errorf("ReadAll() = %v", err)
} else if want, got := "Hello there\n", string(body); got != want {
t.Errorf("ReadAll() = %v, wanted %v", got, want)
}
}
if !found {
t.Error("Didn't find expected file in tarball")
}
})
// Check that the entrypoint of the image is configured to invoke our Go application
t.Run("check entrypoint", func(t *testing.T) {
cfg, err := img.ConfigFile()
if err != nil {
t.Errorf("ConfigFile() = %v", err)
}
entrypoint := cfg.Config.Entrypoint
if got, want := len(entrypoint), 1; got != want {
t.Errorf("len(entrypoint) = %v, want %v", got, want)
}
if got, want := entrypoint[0], "/ko-app/test"; got != want {
t.Errorf("entrypoint = %v, want %v", got, want)
}
})
// Check that the environment contains the KO_DATA_PATH environment variable.
t.Run("check KO_DATA_PATH env var", func(t *testing.T) {
cfg, err := img.ConfigFile()
if err != nil {
t.Errorf("ConfigFile() = %v", err)
}
found := false
for _, entry := range cfg.Config.Env {
if entry == "KO_DATA_PATH="+kodataRoot {
found = true
}
}
if !found {
t.Error("Didn't find expected file in tarball.")
}
})
t.Run("check creation time", func(t *testing.T) {
cfg, err := img.ConfigFile()
if err != nil {
t.Errorf("ConfigFile() = %v", err)
}
actual := cfg.Created
if actual.Time != creationTime.Time {
t.Errorf("created = %v, want %v", actual, creationTime)
}
})
}