diff --git a/integration/integration_test.go b/integration/integration_test.go index 7d5cdc0fc..f79f736f3 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -272,28 +272,8 @@ func getBranchCommitAndURL() (branch, commit, url string) { return } -func DockerGitRepo(url string, commit string, branch string) string { - ref := "" - if commit != "" { - ref = "#" + commit - } else if branch != "" { - ref = "#" + branch - } - return fmt.Sprintf("https://%s.git%s", url, ref) -} - -func KanikoGitRepo(url string, commit string, branch string) string { - ref := "" - if commit != "" { - ref = "#" + commit - } else if branch != "" { - ref = "#refs/heads/" + branch - } - return fmt.Sprintf("git://%s.git%s", url, ref) -} - -func testGitBuildcontextHelper(t *testing.T, url string, commit string, branch string, imageName string) { - t.Log("testGitBuildcontextHelper repo", url) +func testGitBuildcontextHelper(t *testing.T, dockerRef, kanikoRef, imageName string, kanikoExtraFlags ...string) { + t.Log("testGitBuildcontextHelper docker", dockerRef, "kaniko", kanikoRef) dockerfile := fmt.Sprintf("%s/%s/Dockerfile_test_run_2", integrationPath, dockerfilesPath) // Build with docker @@ -304,7 +284,7 @@ func testGitBuildcontextHelper(t *testing.T, url string, commit string, branch s "--push", "-t", dockerImage, "-f", dockerfile, - DockerGitRepo(url, commit, branch), + dockerRef, }...) out, err := RunCommandWithoutTest(dockerCmd) if err != nil { @@ -319,7 +299,8 @@ func testGitBuildcontextHelper(t *testing.T, url string, commit string, branch s dockerRunFlags = append(dockerRunFlags, ExecutorImage, "-f", dockerfile, "-d", kanikoImage, - "-c", KanikoGitRepo(url, commit, branch)) + "-c", kanikoRef) + dockerRunFlags = append(dockerRunFlags, kanikoExtraFlags...) kanikoCmd := exec.Command("docker", dockerRunFlags...) @@ -338,7 +319,11 @@ func testGitBuildcontextHelper(t *testing.T, url string, commit string, branch s func TestGitBuildcontext(t *testing.T) { t.Parallel() branch, _, url := getBranchCommitAndURL() - testGitBuildcontextHelper(t, url, "", branch, "Dockerfile_test_git_branch") + testGitBuildcontextHelper(t, + fmt.Sprintf("https://%s.git#%s", url, branch), + fmt.Sprintf("git://%s.git#refs/heads/%s", url, branch), + "Dockerfile_test_git_branch", + ) } // TestGitBuildcontextNoRef builds without any commit / branch reference @@ -349,7 +334,11 @@ func TestGitBuildcontextNoRef(t *testing.T) { t.Skip("Docker's behavior is to assume a 'master' branch, which the Kaniko repo doesn't have") t.Parallel() _, _, url := getBranchCommitAndURL() - testGitBuildcontextHelper(t, url, "", "", "Dockerfile_test_git_noref") + testGitBuildcontextHelper(t, + fmt.Sprintf("https://%s.git", url), + fmt.Sprintf("git://%s.git", url), + "Dockerfile_test_git_noref", + ) } // TestGitBuildcontextExplicitCommit uses an explicit commit hash instead of named reference @@ -359,7 +348,11 @@ func TestGitBuildcontextNoRef(t *testing.T) { func TestGitBuildcontextExplicitCommit(t *testing.T) { t.Parallel() _, commit, url := getBranchCommitAndURL() - testGitBuildcontextHelper(t, url, commit, "", "Dockerfile_test_git_commit") + testGitBuildcontextHelper(t, + fmt.Sprintf("https://%s.git#%s", url, commit), + fmt.Sprintf("git://%s.git#%s", url, commit), + "Dockerfile_test_git_commit", + ) } func TestGitBuildcontextSubPath(t *testing.T) { @@ -375,7 +368,7 @@ func TestGitBuildcontextSubPath(t *testing.T) { "--push", "-t", dockerImage, "-f", filepath.Join(integrationPath, dockerfilesPath, dockerfile), - DockerGitRepo(url, "", branch), + fmt.Sprintf("https://%s.git#%s", url, branch), }...) out, err := RunCommandWithoutTest(dockerCmd) if err != nil { @@ -392,7 +385,7 @@ func TestGitBuildcontextSubPath(t *testing.T) { ExecutorImage, "-f", dockerfile, "-d", kanikoImage, - "-c", KanikoGitRepo(url, "", branch), + "-c", fmt.Sprintf("git://%s.git#refs/heads/%s", url, branch), "--context-sub-path", filepath.Join(integrationPath, dockerfilesPath), ) @@ -406,6 +399,46 @@ func TestGitBuildcontextSubPath(t *testing.T) { containerDiff(t, dockerImage, kanikoImage, "--semantic", "--extra-ignore-file-content", "--extra-ignore-layer-length-mismatch") } +// TestGitBuildcontextPlainBranch exercises a bare branch name with no +// refs/heads/ prefix (else branch in UnpackTarFromBuildContext around line 88). +func TestGitBuildcontextPlainBranch(t *testing.T) { + t.Parallel() + branch, _, url := getBranchCommitAndURL() + testGitBuildcontextHelper(t, + fmt.Sprintf("https://%s.git#%s", url, branch), + fmt.Sprintf("git://%s.git#%s", url, branch), + "Dockerfile_test_git_plain_branch", + ) +} + +// TestGitBuildcontextTag exercises a refs/tags/ ref, which goes through +// the post-clone fetch flow (lines 84-87 and 108-124) instead of the direct +// clone-by-ref path used for refs/heads/. +func TestGitBuildcontextTag(t *testing.T) { + t.Parallel() + _, _, url := getBranchCommitAndURL() + tag := "refs/tags/v1.27.5" + testGitBuildcontextHelper(t, + fmt.Sprintf("https://%s.git#%s", url, tag), + fmt.Sprintf("git://%s.git#%s", url, tag), + "Dockerfile_test_git_tag", + ) +} + +// TestGitBuildcontextBranchFlag exercises the --git=branch= flag, which +// triggers getGitReferenceName (lines 94-100 and 152-179). Docker has no +// equivalent flag, so the docker side puts the branch in the URL fragment. +func TestGitBuildcontextBranchFlag(t *testing.T) { + t.Parallel() + branch, _, url := getBranchCommitAndURL() + testGitBuildcontextHelper(t, + fmt.Sprintf("https://%s.git#%s", url, branch), + fmt.Sprintf("git://%s.git", url), + "Dockerfile_test_git_branch_flag", + "--git=branch="+branch, + ) +} + func TestBuildViaRegistryMirrors(t *testing.T) { t.Parallel() branch, _, url := getBranchCommitAndURL() @@ -419,7 +452,7 @@ func TestBuildViaRegistryMirrors(t *testing.T) { "--push", "-t", dockerImage, "-f", dockerfile, - DockerGitRepo(url, "", branch), + fmt.Sprintf("https://%s.git#%s", url, branch), }...) out, err := RunCommandWithoutTest(dockerCmd) if err != nil { @@ -436,7 +469,7 @@ func TestBuildViaRegistryMirrors(t *testing.T) { "-d", kanikoImage, "--registry-mirror", "doesnotexist.example.com", "--registry-mirror", "us-mirror.gcr.io", - "-c", KanikoGitRepo(url, "", branch)) + "-c", fmt.Sprintf("git://%s.git#refs/heads/%s", url, branch)) kanikoCmd := exec.Command("docker", dockerRunFlags...) @@ -461,7 +494,7 @@ func TestBuildViaRegistryMap(t *testing.T) { "-t", dockerImage, "-f", dockerfile, "--push", - DockerGitRepo(url, "", branch), + fmt.Sprintf("https://%s.git#%s", url, branch), }...) out, err := RunCommandWithoutTest(dockerCmd) if err != nil { @@ -478,7 +511,7 @@ func TestBuildViaRegistryMap(t *testing.T) { "-d", kanikoImage, "--registry-map", "index.docker.io=doesnotexist.example.com", "--registry-map", "index.docker.io=us-mirror.gcr.io", - "-c", KanikoGitRepo(url, "", branch)) + "-c", fmt.Sprintf("git://%s.git#refs/heads/%s", url, branch)) kanikoCmd := exec.Command("docker", dockerRunFlags...) @@ -505,7 +538,7 @@ func TestBuildSkipFallback(t *testing.T) { "-d", kanikoImage, "--registry-mirror", "doesnotexist.example.com", "--skip-default-registry-fallback", - "-c", KanikoGitRepo(url, "", branch)) + "-c", fmt.Sprintf("git://%s.git#refs/heads/%s", url, branch)) kanikoCmd := exec.Command("docker", dockerRunFlags...) @@ -529,7 +562,7 @@ func TestKanikoDir(t *testing.T) { "-t", dockerImage, "-f", dockerfile, "--push", - DockerGitRepo(url, "", branch), + fmt.Sprintf("https://%s.git#%s", url, branch), }...) out, err := RunCommandWithoutTest(dockerCmd) if err != nil { @@ -544,7 +577,7 @@ func TestKanikoDir(t *testing.T) { dockerRunFlags = append(dockerRunFlags, ExecutorImage, "-f", dockerfile, "-d", kanikoImage, - "-c", KanikoGitRepo(url, "", branch)) + "-c", fmt.Sprintf("git://%s.git#refs/heads/%s", url, branch)) kanikoCmd := exec.Command("docker", dockerRunFlags...) kanikoCmd.Env = append(kanikoCmd.Env, "KANIKO_DIR=/not-kaniko") @@ -573,7 +606,7 @@ func TestBuildWithLabels(t *testing.T) { "-t", dockerImage, "-f", dockerfile, "--label", testLabel, - DockerGitRepo(url, "", branch), + fmt.Sprintf("https://%s.git#%s", url, branch), }...) out, err := RunCommandWithoutTest(dockerCmd) if err != nil { @@ -589,7 +622,7 @@ func TestBuildWithLabels(t *testing.T) { "-f", dockerfile, "-d", kanikoImage, "--label", testLabel, - "-c", KanikoGitRepo(url, "", branch), + "-c", fmt.Sprintf("git://%s.git#refs/heads/%s", url, branch), ) kanikoCmd := exec.Command("docker", dockerRunFlags...) @@ -614,7 +647,7 @@ func TestBuildWithHTTPError(t *testing.T) { "build", "-t", dockerImage, "-f", dockerfile, - DockerGitRepo(url, "", branch), + fmt.Sprintf("https://%s.git#%s", url, branch), }...) out, err := RunCommandWithoutTest(dockerCmd) if err == nil { @@ -629,7 +662,7 @@ func TestBuildWithHTTPError(t *testing.T) { dockerRunFlags = append(dockerRunFlags, ExecutorImage, "-f", dockerfile, "-d", kanikoImage, - "-c", KanikoGitRepo(url, "", branch), + "-c", fmt.Sprintf("git://%s.git#refs/heads/%s", url, branch), ) kanikoCmd := exec.Command("docker", dockerRunFlags...) @@ -880,6 +913,7 @@ func TestWarmerTwice(t *testing.T) { // Start a sleeping warmer container dockerRunFlags := []string{"run", "--net=host"} dockerRunFlags = addServiceAccountFlags(dockerRunFlags, config.serviceAccount) + dockerRunFlags = addCoverageFlags(dockerRunFlags) for _, envVariable := range WarmerEnv { dockerRunFlags = append(dockerRunFlags, "-e", envVariable) } @@ -1059,7 +1093,7 @@ func TestBuildWithAnnotations(t *testing.T) { "-t", dockerImage, "-f", dockerfile, "--annotation", fmt.Sprintf("%s=%s", annotationKey, annotationValue), - DockerGitRepo(url, "", branch), + fmt.Sprintf("https://%s.git#%s", url, branch), ) out, err := RunCommandWithoutTest(dockerCmd) if err != nil { @@ -1075,7 +1109,7 @@ func TestBuildWithAnnotations(t *testing.T) { "-f", dockerfile, "-d", kanikoImage, "--annotation", fmt.Sprintf("%s=%s", annotationKey, annotationValue), - "-c", KanikoGitRepo(url, "", branch), + "-c", fmt.Sprintf("git://%s.git#refs/heads/%s", url, branch), ) kanikoCmd := exec.Command("docker", dockerRunFlags...) out, err = RunCommandWithoutTest(kanikoCmd) diff --git a/integration/integration_with_stdin_test.go b/integration/integration_with_stdin_test.go index e7e6d5059..a94ed6f9e 100644 --- a/integration/integration_with_stdin_test.go +++ b/integration/integration_with_stdin_test.go @@ -104,6 +104,7 @@ func TestBuildWithStdin(t *testing.T) { dockerRunFlags := []string{"run", "--interactive", "--net=host", "-v", cwd + ":/workspace"} dockerRunFlags = addServiceAccountFlags(dockerRunFlags, config.serviceAccount) + dockerRunFlags = addCoverageFlags(dockerRunFlags) dockerRunFlags = append(dockerRunFlags, ExecutorImage, "-f", dockerfile, diff --git a/integration/integration_with_tarball_test.go b/integration/integration_with_tarball_test.go new file mode 100644 index 000000000..7fd0526f7 --- /dev/null +++ b/integration/integration_with_tarball_test.go @@ -0,0 +1,136 @@ +/* +Copyright 2026 OSS Container Tools + +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 integration + +import ( + "crypto/tls" + "fmt" + "net" + "net/http" + "os" + "os/exec" + "path/filepath" + "testing" +) + +// makeDockerfileTarball builds a gzipped tarball at a fresh temp path +// containing just Dockerfile_test_run_2 at the tarball root. +func makeDockerfileTarball(t *testing.T) string { + t.Helper() + tarPath := filepath.Join(t.TempDir(), "context.tar.gz") + tarCmd := exec.Command("tar", "-czf", tarPath, "Dockerfile_test_run_2") + tarCmd.Dir = dockerfilesPath + out, err := tarCmd.CombinedOutput() + if err != nil { + t.Fatalf("tar: %v\n%s", err, out) + } + return tarPath +} + +// testBuildFromTarballHelper builds the image with docker (piping tarPath over +// stdin so the daemon doesn't have to handle whatever transport kaniko uses) +// and with kaniko (using kanikoContextRef and any extra docker-run flags such +// as volume mounts), then compares the two images. +func testBuildFromTarballHelper(t *testing.T, imageName, tarPath, kanikoContextRef string, extraDockerRunFlags ...string) { + t.Helper() + + dockerImage := GetDockerImage(config.imageRepo, imageName) + f, err := os.Open(tarPath) + if err != nil { + t.Fatalf("open tarball: %v", err) + } + defer f.Close() + dockerCmd := exec.Command("docker", "build", "--push", + "-f", "Dockerfile_test_run_2", "-t", dockerImage, "-") + dockerCmd.Stdin = f + out, err := RunCommandWithoutTest(dockerCmd) + if err != nil { + t.Fatalf("docker build failed: %v\n%s", err, out) + } + + kanikoImage := GetKanikoImage(config.imageRepo, imageName) + dockerRunFlags := []string{"run", "--net=host"} + dockerRunFlags = append(dockerRunFlags, extraDockerRunFlags...) + dockerRunFlags = addServiceAccountFlags(dockerRunFlags, config.serviceAccount) + dockerRunFlags = addCoverageFlags(dockerRunFlags) + dockerRunFlags = append(dockerRunFlags, ExecutorImage, + "-c", kanikoContextRef, + "-f", "Dockerfile_test_run_2", + "-d", kanikoImage, + ) + kanikoCmd := exec.Command("docker", dockerRunFlags...) + out, err = RunCommandWithoutTest(kanikoCmd) + if err != nil { + t.Fatalf("kaniko build failed: %v\n%s", err, out) + } + + containerDiff(t, dockerImage, kanikoImage, "--semantic", + "--extra-ignore-file-content", "--extra-ignore-layer-length-mismatch") +} + +// TestHTTPSBuildcontext exercises the https:// tarball build context. The +// scripts/setup-tls-registry-creds.sh helper (invoked by k3s-setup) generates a +// self-signed cert for IP:127.0.0.2, which we reuse to serve the tarball over +// TLS on a free port. Kaniko trusts the cert via a bind mount into its +// SSL_CERT_DIR. +func TestHTTPSBuildcontext(t *testing.T) { + t.Parallel() + + caCert := os.Getenv("TLS_REGISTRY_CERT") + if caCert == "" { + t.Fatal("TLS_REGISTRY_CERT not set") + } + keyFile := filepath.Join(filepath.Dir(caCert), "tls.key") + cert, err := tls.LoadX509KeyPair(caCert, keyFile) + if err != nil { + t.Fatalf("load TLS keypair: %v", err) + } + + tarPath := makeDockerfileTarball(t) + + listener, err := net.Listen("tcp", "127.0.0.2:0") + if err != nil { + t.Fatalf("listen: %v", err) + } + defer listener.Close() + + srv := &http.Server{ + Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, tarPath) + }), + TLSConfig: &tls.Config{Certificates: []tls.Certificate{cert}}, + } + go func() { _ = srv.ServeTLS(listener, "", "") }() + defer srv.Close() + + tarballURL := fmt.Sprintf("https://%s/context.tar.gz", listener.Addr().String()) + + testBuildFromTarballHelper(t, "Dockerfile_test_https", tarPath, tarballURL, + "-v", caCert+":/kaniko/ssl/certs/test-server-ca.crt:ro") +} + +// TestTarFileBuildcontext exercises the tar:// build context — the +// non-stdin branch of pkg/buildcontext/tar.go. The tarball is created on the +// host and bind-mounted into the kaniko container. +func TestTarFileBuildcontext(t *testing.T) { + t.Parallel() + + tarPath := makeDockerfileTarball(t) + const containerTarPath = "/workspace/context.tar.gz" + testBuildFromTarballHelper(t, "Dockerfile_test_tar_file", tarPath, "tar://"+containerTarPath, + "-v", tarPath+":"+containerTarPath+":ro") +}