Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: simpler local integration tests #2110

Merged
9 changes: 9 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
integration/**/*
scripts/**/*
hack/**/*
examples/**/*
docs/**/*
.github/**/*
logo/**/*
out/**/*

24 changes: 9 additions & 15 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ find . -name "*.go" | grep -v vendor/ | xargs gofmt -l -s -w

Currently the integration tests that live in [`integration`](./integration) can be run against your own gcloud space or a local registry.

These tests will be kicked off by [reviewers](#reviews) for submitted PRs using GitHub Actions.

In either case, you will need the following tools:

* [`container-diff`](https://github.com/GoogleContainerTools/container-diff#installation)
Expand Down Expand Up @@ -134,33 +136,25 @@ go test ./integration -v --bucket $GCS_BUCKET --repo $IMAGE_REPO -run TestLayers

These tests will be kicked off by [reviewers](#reviews) for submitted PRs by the kokoro task.

#### Local repository
#### Local integration tests

To run integration tests locally against a local registry, install a local docker registry
To run integration tests locally against a local registry and gcs bucket, set the LOCAL environment variable

```shell
docker run --rm -d -p 5000:5000 --name registry registry:2
LOCAL=1 make integration-test
```

Then export the `IMAGE_REPO` variable with the `localhost:5000`value

```shell
export IMAGE_REPO=localhost:5000
```
#### Running integration tests for a specific dockerfile

And run the integration tests
In order to test only specific dockerfiles during local integration testing, you can specify a pattern to match against inside the integration/dockerfiles directory.

```shell
make integration-test
DOCKERFILE_PATTERN="Dockerfile_test_add*" make integration-test-run
```

You can also run tests with `go test`, for example to run tests individually:
This will only run dockerfiles that match the pattern `Dockerfile_test_add*`

```shell
go test ./integration -v --repo localhost:5000 -run TestLayers/test_layer_Dockerfile_test_copy_bucket
```

These tests will be kicked off by [reviewers](#reviews) for submitted PRs using GitHub Actions.

### Benchmarking

Expand Down
2 changes: 1 addition & 1 deletion integration/benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func TestSnapshotBenchmark(t *testing.T) {
buildArgs := []string{"--build-arg", fmt.Sprintf("NUM=%d", num)}
var benchmarkDir string
benchmarkDir, *err = buildKanikoImage(t.Logf, "", dockerfile,
buildArgs, []string{}, kanikoImage, contextDir, config.gcsBucket,
buildArgs, []string{}, kanikoImage, contextDir, config.gcsBucket, config.gcsClient,
config.serviceAccount, false)
if *err != nil {
return
Expand Down
8 changes: 7 additions & 1 deletion integration/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ limitations under the License.

package integration

import "strings"
import (
"strings"

"cloud.google.com/go/storage"
)

type integrationTestConfig struct {
gcsBucket string
Expand All @@ -25,6 +29,8 @@ type integrationTestConfig struct {
hardlinkBaseImage string
serviceAccount string
dockerMajorVersion int
gcsClient *storage.Client
dockerfilesPattern string
}

const gcrRepoPrefix string = "gcr.io/"
Expand Down
75 changes: 0 additions & 75 deletions integration/gcs.go

This file was deleted.

27 changes: 19 additions & 8 deletions integration/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package integration

import (
"bytes"
"context"
"fmt"
"io/ioutil"
"os"
Expand All @@ -30,8 +31,10 @@ import (
"testing"
"time"

"cloud.google.com/go/storage"
"github.com/GoogleContainerTools/kaniko/pkg/timing"
"github.com/GoogleContainerTools/kaniko/pkg/util"
"github.com/GoogleContainerTools/kaniko/pkg/util/bucket"
)

const (
Expand Down Expand Up @@ -157,13 +160,16 @@ func GetVersionedKanikoImage(imageRepo, dockerfile string, version int) string {
return strings.ToLower(imageRepo + kanikoPrefix + dockerfile + strconv.Itoa(version))
}

// FindDockerFiles will look for test docker files in the directory dockerfilesPath.
// These files must start with `Dockerfile_test`. If the file is one we are intentionally
// FindDockerFiles will look for test docker files in the directory dir
// and match the files against dockerfilesPattern.
// If the file is one we are intentionally
// skipping, it will not be included in the returned list.
func FindDockerFiles(dockerfilesPath string) ([]string, error) {
allDockerfiles, err := filepath.Glob(path.Join(dockerfilesPath, "Dockerfile_test*"))
func FindDockerFiles(dir, dockerfilesPattern string) ([]string, error) {
pattern := filepath.Join(dir, dockerfilesPattern)
fmt.Printf("finding docker images with pattern %v\n", pattern)
allDockerfiles, err := filepath.Glob(pattern)
if err != nil {
return []string{}, fmt.Errorf("Failed to find docker files at %s: %w", dockerfilesPath, err)
return []string{}, fmt.Errorf("Failed to find docker files with pattern %s: %w", dockerfilesPattern, err)
}

var dockerfiles []string
Expand Down Expand Up @@ -285,7 +291,7 @@ func (d *DockerFileBuilder) BuildImageWithContext(t *testing.T, config *integrat
if _, present := d.filesBuilt[dockerfile]; present {
return nil
}
gcsBucket, serviceAccount, imageRepo := config.gcsBucket, config.serviceAccount, config.imageRepo
gcsBucket, gcsClient, serviceAccount, imageRepo := config.gcsBucket, config.gcsClient, config.serviceAccount, config.imageRepo

var buildArgs []string
buildArgFlag := "--build-arg"
Expand Down Expand Up @@ -318,7 +324,7 @@ func (d *DockerFileBuilder) BuildImageWithContext(t *testing.T, config *integrat
kanikoImage := GetKanikoImage(imageRepo, dockerfile)
timer = timing.Start(dockerfile + "_kaniko")
if _, err := buildKanikoImage(t.Logf, dockerfilesPath, dockerfile, buildArgs, additionalKanikoFlags, kanikoImage,
contextDir, gcsBucket, serviceAccount, true); err != nil {
contextDir, gcsBucket, gcsClient, serviceAccount, true); err != nil {
return err
}
timing.DefaultRun.Stop(timer)
Expand Down Expand Up @@ -443,6 +449,7 @@ func buildKanikoImage(
kanikoImage string,
contextDir string,
gcsBucket string,
gcsClient *storage.Client,
serviceAccount string,
shdUpload bool,
) (string, error) {
Expand All @@ -457,7 +464,11 @@ func buildKanikoImage(
benchmarkFile := path.Join(benchmarkDir, dockerfile)
fileName := fmt.Sprintf("run_%s_%s", time.Now().Format("2006-01-02-15:04"), dockerfile)
dst := path.Join("benchmarks", fileName)
defer UploadFileToBucket(gcsBucket, benchmarkFile, dst)
file, err := os.Open(benchmarkFile)
if err != nil {
return "", err
}
defer bucket.Upload(context.Background(), gcsBucket, dst, file, gcsClient)
}
}

Expand Down
68 changes: 53 additions & 15 deletions integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package integration

import (
"context"
"encoding/json"
"flag"
"fmt"
Expand All @@ -33,9 +34,11 @@ import (
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/daemon"
"github.com/pkg/errors"
"google.golang.org/api/option"

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

Expand Down Expand Up @@ -86,22 +89,33 @@ func getDockerMajorVersion() int {

func launchTests(m *testing.M) (int, error) {
if config.isGcrRepository() {
contextFile, err := CreateIntegrationTarball()
contextFilePath, err := CreateIntegrationTarball()
if err != nil {
return 1, errors.Wrap(err, "Failed to create tarball of integration files for build context")
}

fileInBucket, err := UploadFileToBucket(config.gcsBucket, contextFile, contextFile)
bucketName, item, err := bucket.GetNameAndFilepathFromURI(config.gcsBucket)
if err != nil {
return 1, errors.Wrap(err, "failed to get bucket name from uri")
}
contextFile, err := os.Open(contextFilePath)
if err != nil {
return 1, fmt.Errorf("failed to read file at path %v: %w", contextFilePath, err)
}
err = bucket.Upload(context.Background(), bucketName, item, contextFile, config.gcsClient)
if err != nil {
return 1, errors.Wrap(err, "Failed to upload build context")
}

if err = os.Remove(contextFile); err != nil {
return 1, errors.Wrap(err, fmt.Sprintf("Failed to remove tarball at %s", contextFile))
if err = os.Remove(contextFilePath); err != nil {
return 1, errors.Wrap(err, fmt.Sprintf("Failed to remove tarball at %s", contextFilePath))
}

RunOnInterrupt(func() { DeleteFromBucket(fileInBucket) })
defer DeleteFromBucket(fileInBucket)
deleteFunc := func() {
bucket.Delete(context.Background(), bucketName, item, config.gcsClient)
}
RunOnInterrupt(deleteFunc)
defer deleteFunc()
}
if err := buildRequiredImages(); err != nil {
return 1, errors.Wrap(err, "Error while building images")
Expand All @@ -119,18 +133,18 @@ func TestMain(m *testing.M) {
os.Exit(1)
}

if allDockerfiles, err = FindDockerFiles(dockerfilesPath); err != nil {
config = initIntegrationTestConfig()
if allDockerfiles, err = FindDockerFiles(dockerfilesPath, config.dockerfilesPattern); err != nil {
fmt.Println("Coudn't create map of dockerfiles", err)
os.Exit(1)
} else {
config = initIntegrationTestConfig()
exitCode, err := launchTests(m)
if err != nil {
fmt.Println(err)
}
os.Exit(exitCode)
}

exitCode, err := launchTests(m)
if err != nil {
fmt.Println(err)
}
os.Exit(exitCode)

}

func buildRequiredImages() error {
Expand Down Expand Up @@ -859,9 +873,16 @@ func (i imageDetails) String() string {

func initIntegrationTestConfig() *integrationTestConfig {
var c integrationTestConfig

var gcsEndpoint string
var disableGcsAuth bool
flag.StringVar(&c.gcsBucket, "bucket", "gs://kaniko-test-bucket", "The gcs bucket argument to uploaded the tar-ed contents of the `integration` dir to.")
flag.StringVar(&c.imageRepo, "repo", "gcr.io/kaniko-test", "The (docker) image repo to build and push images to during the test. `gcloud` must be authenticated with this repo or serviceAccount must be set.")
flag.StringVar(&c.serviceAccount, "serviceAccount", "", "The path to the service account push images to GCR and upload/download files to GCS.")
flag.StringVar(&gcsEndpoint, "gcs-endpoint", "", "Custom endpoint for GCS. Used for local integration tests")
flag.BoolVar(&disableGcsAuth, "disable-gcs-auth", false, "Disable GCS Authentication. Used for local integration tests")
// adds the possibility to run a single dockerfile. This is useful since running all images can exhaust the dockerhub pull limit
flag.StringVar(&c.dockerfilesPattern, "dockerfiles-pattern", "Dockerfile_test*", "The pattern to match dockerfiles with")
flag.Parse()

if len(c.serviceAccount) > 0 {
Expand All @@ -886,14 +907,31 @@ func initIntegrationTestConfig() *integrationTestConfig {
if !strings.HasSuffix(c.imageRepo, "/") {
c.imageRepo = c.imageRepo + "/"
}

if c.gcsBucket != "" {
var opts []option.ClientOption
if gcsEndpoint != "" {
opts = append(opts, option.WithEndpoint(gcsEndpoint))
}
if disableGcsAuth {
opts = append(opts, option.WithoutAuthentication())
}

gcsClient, err := bucket.NewClient(context.Background(), opts...)
if err != nil {
log.Fatalf("Could not create a new Google Storage Client: %s", err)
}
c.gcsClient = gcsClient
}

c.dockerMajorVersion = getDockerMajorVersion()
c.onbuildBaseImage = c.imageRepo + "onbuild-base:latest"
c.hardlinkBaseImage = c.imageRepo + "hardlink-base:latest"
return &c
}

func meetsRequirements() bool {
requiredTools := []string{"container-diff", "gsutil"}
requiredTools := []string{"container-diff"}
hasRequirements := true
for _, tool := range requiredTools {
_, err := exec.LookPath(tool)
Expand Down
Loading