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 785b390
Show file tree
Hide file tree
Showing 9 changed files with 196 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)
}
176 changes: 176 additions & 0 deletions pkg/buildcontext/tar_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/*
Copyright 2018 Google LLC
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 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 785b390

Please sign in to comment.