Skip to content

Commit

Permalink
feat(kaniko): add unit test about valid, invalid and non existing tar.gz
Browse files Browse the repository at this point in the history
  • Loading branch information
JordanGoasdoue authored and goasdoue committed Mar 12, 2020
1 parent f3d6b23 commit 78df15b
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 139 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ When running kaniko, use the `--context` flag with the appropriate prefix to spe
| Source | Prefix | Example |
|---------|---------|---------|
| Local Directory | dir://[path to a directory in the kaniko container] | `dir:///workspace` |
| Local File | file://[path to a .tar.gz in the kaniko container] | `file://path/to/context.tar.gz` |
| Local Tar Gz File | tar://[path to a .tar.gz in the kaniko container] | `tar://path/to/context.tar.gz` |
| GCS Bucket | gs://[bucket name]/[path to .tar.gz] | `gs://kaniko-bucket/path/to/context.tar.gz` |
| S3 Bucket | s3://[bucket name]/[path to .tar.gz] | `s3://kaniko-bucket/path/to/context.tar.gz` |
| Azure Blob Storage| https://[account].[azureblobhostsuffix]/[container]/[path to .tar.gz] | `https://myaccount.blob.core.windows.net/container/path/to/context.tar.gz` |
Expand Down
20 changes: 0 additions & 20 deletions integration/dockerfiles-to-tar-gz/Dockerfile_echo_hey

This file was deleted.

21 changes: 0 additions & 21 deletions integration/dockerfiles-to-tar-gz/Dockerfile_echo_home

This file was deleted.

87 changes: 0 additions & 87 deletions integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,16 @@ limitations under the License.
package integration

import (
"compress/gzip"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"math"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"sync"
"testing"
"time"

Expand Down Expand Up @@ -312,89 +308,6 @@ func TestBuildViaRegistryMirror(t *testing.T) {
checkContainerDiffOutput(t, diff, expected)
}

func TestBuildWithLocalTars(t *testing.T) {
sourceDir := "dockerfiles-to-tar-gz"

fi, err := ioutil.ReadDir(sourceDir)
if err != nil {
t.Errorf("Failed to TestBuildWithLocalTars: can't list files from %s: %v", sourceDir, err)
}

var wg sync.WaitGroup

for _, f := range fi {
wg.Add(1)
dockerfile := filepath.Join(sourceDir, f.Name())
tarPath := fmt.Sprintf("%s.tar.gz", dockerfile)

// Create Tar Gz File with dockerfile inside
go func(wg *sync.WaitGroup) {
defer wg.Done()
tarFile, err := os.Create(tarPath)
if err != nil {
t.Errorf("Failed to TestBuildWithLocalTars: can't create %s: %v", tarPath, err)
}
defer tarFile.Close()

gw := gzip.NewWriter(tarFile)
defer gw.Close()

tw := util.NewTar(gw)
defer tw.Close()

if err := tw.AddFileToTar(dockerfile); err != nil {
t.Errorf("Failed to TestBuildWithLocalTars: can't add %s to %s: %v", f.Name(), tarPath, err)
}
}(&wg)
// Waiting for the Tar Gz file creation to be done before moving on
wg.Wait()

// Build with kaniko
kanikoImage := GetKanikoImage(config.imageRepo, f.Name())

_, ex, _, _ := runtime.Caller(0)
cwd := filepath.Dir(ex)

dockerRunFlags := []string{"run", "--net=host", "-v", cwd + ":/workspace"}
dockerRunFlags = addServiceAccountFlags(dockerRunFlags, config.serviceAccount)
dockerRunFlags = append(dockerRunFlags, ExecutorImage,
"-f", dockerfile,
"-d", kanikoImage,
"-c", fmt.Sprintf("file://%s", tarPath))

kanikoCmd := exec.Command("docker", dockerRunFlags...)

out, err := RunCommandWithoutTest(kanikoCmd)
if err != nil {
t.Errorf("Failed to TestBuildWithLocalTars: can't build image %s with docker command %q: %v %s", kanikoImage, kanikoCmd.Args, err, string(out))
}

// Build with docker
dockerImage := GetDockerImage(config.imageRepo, f.Name())
dockerArgs := []string{
"build",
"-t", dockerImage,
"-f", dockerfile,
".",
}

dockerCmd := exec.Command("docker", dockerArgs...)
out, err = RunCommandWithoutTest(dockerCmd)
if err != nil {
t.Errorf("Failed to TestBuildWithLocalTars: can't build image %s with docker command %q: %v %s", dockerImage, dockerCmd.Args, err, string(out))
}

if err := os.Remove(tarPath); err != nil {
t.Errorf("Failed to TestBuildWithLocalTars: can't remove %s: %v", tarPath, err)
}

diff := containerDiff(t, daemonPrefix+dockerImage, kanikoImage, "--no-cache")

expected := fmt.Sprintf(emptyContainerDiff, dockerImage, kanikoImage, dockerImage, kanikoImage)
checkContainerDiffOutput(t, diff, expected)
}
}

func TestLayers(t *testing.T) {
offset := map[string]int{
"Dockerfile_test_add": 12,
Expand Down
10 changes: 7 additions & 3 deletions pkg/buildcontext/buildcontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ import (
"github.com/GoogleContainerTools/kaniko/pkg/util"
)

const (
TarBuildContextPrefix = "tar://"
)

// BuildContext unifies calls to download and unpack the build context.
type BuildContext interface {
// Unpacks a build context and returns the directory where it resides
Expand Down Expand Up @@ -51,8 +55,8 @@ func GetBuildContext(srcContext string) (BuildContext, error) {
return &AzureBlob{context: srcContext}, nil
}
return nil, errors.New("url provided for https context is not in a supported format, please use the https url for Azure Blob Storage")
case constants.FileBuildContextPrefix:
return &File{context: context}, nil
case TarBuildContextPrefix:
return &Tar{context: context}, nil
}
return nil, errors.New("unknown build context prefix provided, please use one of the following: gs://, dir://, s3://, git://, https://")
return nil, errors.New("unknown build context prefix provided, please use one of the following: gs://, dir://, tar://, s3://, git://, https://")
}
13 changes: 7 additions & 6 deletions pkg/buildcontext/file.go → pkg/buildcontext/tar.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,20 @@ import (

"github.com/GoogleContainerTools/kaniko/pkg/constants"
"github.com/GoogleContainerTools/kaniko/pkg/util"
"github.com/pkg/errors"
)

// File unifies calls to download and unpack the build context.
type File struct {
// Tar unifies calls to download and unpack the build context.
type Tar struct {
context string
}

// UnpackTarFromBuildContext untar the tar file
func (f *File) UnpackTarFromBuildContext() (string, error) {
// UnpackTarFromBuildContext unpack the compressed tar file
func (t *Tar) UnpackTarFromBuildContext() (string, error) {
directory := constants.BuildContextDir
if err := os.MkdirAll(directory, 0750); err != nil {
return "", err
return "", errors.Wrap(err, "unpacking tar from build context")
}

return directory, util.UnpackCompressedTar(f.context, directory)
return directory, util.UnpackCompressedTar(t.context, directory)
}
160 changes: 160 additions & 0 deletions pkg/buildcontext/tar_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package buildcontext

import (
"compress/gzip"
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"os"
"path/filepath"
"runtime"
"sync"
"testing"

"github.com/GoogleContainerTools/kaniko/pkg/util"
"github.com/GoogleContainerTools/kaniko/testutil"
)

func TestBuildWithLocalTar(t *testing.T) {
_, ex, _, _ := runtime.Caller(0)
cwd := filepath.Dir(ex)

testDir := "test_dir"

testDirLongPath := filepath.Join(cwd, testDir)

directoryUncompress := filepath.Join(testDirLongPath, "dir_to_uncompress")

if err := os.MkdirAll(directoryUncompress, 0750); err != nil {
t.Errorf("Failed to create dir_to_uncompress: %v", err)
}

validDockerfile := "Dockerfile_valid"
invalidDockerfile := "Dockerfile_invalid"
nonExistingDockerfile := "Dockerfile_non_existing"

files := map[string]string{
validDockerfile: "FROM debian:9.11\nRUN echo \"valid\"",
invalidDockerfile: "FROM debian:9.11\nRUN echo \"invalid\"",
nonExistingDockerfile: "FROM debian:9.11\nRUN echo \"non_existing\"",
}

if err := testutil.SetupFiles(testDir, files); err != nil {
t.Errorf("Failed to setup files %v on %s: %v", files, testDir, err)
}

if err := os.Chdir(testDir); err != nil {
t.Fatalf("Failed to Chdir on %s: %v", testDir, err)
}

validTarPath := fmt.Sprintf("%s.tar.gz", validDockerfile)
invalidTarPath := fmt.Sprintf("%s.tar.gz", invalidDockerfile)
nonExistingTarPath := fmt.Sprintf("%s.tar.gz", nonExistingDockerfile)

var wg sync.WaitGroup
wg.Add(1)
// Create Tar Gz File with dockerfile inside
go func(wg *sync.WaitGroup) {
defer wg.Done()
validTarFile, err := os.Create(validTarPath)
if err != nil {
t.Errorf("Failed to create %s: %v", validTarPath, err)
}
defer validTarFile.Close()

invalidTarFile, err := os.Create(invalidTarPath)
if err != nil {
t.Errorf("Failed to create %s: %v", invalidTarPath, err)
}
defer invalidTarFile.Close()

gw := gzip.NewWriter(validTarFile)
defer gw.Close()

tw := util.NewTar(gw)
defer tw.Close()

if err := tw.AddFileToTar(validDockerfile); err != nil {
t.Errorf("Failed to add %s to %s: %v", validDockerfile, validTarPath, err)
}
}(&wg)

// Waiting for the Tar Gz file creation to be done before moving on
wg.Wait()

tests := []struct {
dockerfile string
srcContext string
shouldErr bool
expectedErr string
}{
{
dockerfile: validDockerfile,
srcContext: filepath.Join(testDir, validTarPath),
shouldErr: false,
expectedErr: "<nil>",
},
{
dockerfile: invalidDockerfile,
srcContext: filepath.Join(testDir, invalidTarPath),
shouldErr: true,
expectedErr: "EOF",
},
{
dockerfile: nonExistingDockerfile,
srcContext: filepath.Join(testDir, nonExistingTarPath),
shouldErr: true,
expectedErr: fmt.Sprintf("open %s: no such file or directory", filepath.Join(testDirLongPath, nonExistingTarPath)),
},
}

for _, tt := range tests {
t.Run(tt.dockerfile, func(t *testing.T) {
err := util.UnpackCompressedTar(filepath.Join(cwd, tt.srcContext), directoryUncompress)
if err != nil {
if !tt.shouldErr {
t.Errorf("Unexpected error for %s: %v", tt.dockerfile, err)
}
if err.Error() != tt.expectedErr {
t.Errorf("error '%s' and '%s' aren't equal", err.Error(), tt.expectedErr)
}
} else {
if tt.shouldErr {
t.Errorf("Expected error, but returned none for %s", tt.dockerfile)
}
sourceSha, err := getSha(tt.dockerfile)
if err != nil {
t.Fatal(err)
}
destSha, err := getSha(filepath.Join(directoryUncompress, tt.dockerfile))
if err != nil {
t.Fatal(err)
}
if sourceSha != destSha {
t.Errorf("'%s' and '%s' aren't equal", sourceSha, destSha)
}
}
})
}

if err := os.RemoveAll(testDirLongPath); err != nil {
t.Errorf("Failed to remove %s: %v", testDirLongPath, err)
}
}

func getSha(path string) (string, error) {
f, err := os.Open(path)
if err != nil {
return "", err
}
defer f.Close()

hasher := sha256.New()
if _, err := io.Copy(hasher, f); err != nil {
return "", err
}
value := hex.EncodeToString(hasher.Sum(nil))

return value, nil
}
1 change: 0 additions & 1 deletion pkg/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ const (
LocalDirBuildContextPrefix = "dir://"
GitBuildContextPrefix = "git://"
HTTPSBuildContextPrefix = "https://"
FileBuildContextPrefix = "file://"

HOME = "HOME"
// DefaultHOMEValue is the default value Docker sets for $HOME
Expand Down
Loading

0 comments on commit 78df15b

Please sign in to comment.