mirror of
https://github.com/ko-build/ko.git
synced 2024-12-15 09:04:09 +02:00
b0a1702132
* Ignore GoLand files. * Add --disable-optimizations flag to make debugging easier. * Fix unit test (by ignoring the new functionality).
317 lines
8.4 KiB
Go
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)
|
|
}
|
|
})
|
|
}
|