fix(registry_image): consider .dockerignore in image build (#240)

* fix(registry_image): consider .dockerignore in image build
* test: for respecting dockerignore in registry image build
This commit is contained in:
Manuel Vogel 2021-07-07 10:35:18 +02:00 committed by GitHub
parent 0b9790e3d3
commit b759b8f2c9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 110 additions and 1 deletions

View file

@ -20,9 +20,11 @@ import (
"strings" "strings"
"time" "time"
"github.com/docker/cli/cli/command/image/build"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/docker/docker/pkg/fileutils"
"github.com/docker/go-units" "github.com/docker/go-units"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
@ -245,9 +247,10 @@ func buildDockerRegistryImage(ctx context.Context, client *client.Client, buildO
if lastIndex := strings.LastIndexByte(buildContext, ':'); (lastIndex > -1) && (buildContext[lastIndex+1] != filepath.Separator) { if lastIndex := strings.LastIndexByte(buildContext, ':'); (lastIndex > -1) && (buildContext[lastIndex+1] != filepath.Separator) {
buildContext = buildContext[:lastIndex] buildContext = buildContext[:lastIndex]
} }
dockerContextTarPath, err := buildDockerImageContextTar(buildContext) dockerContextTarPath, err := buildDockerImageContextTar(buildContext)
if err != nil { if err != nil {
return fmt.Errorf("unable to build context %v", err) return fmt.Errorf("unable to build context: %v", err)
} }
defer os.Remove(dockerContextTarPath) defer os.Remove(dockerContextTarPath)
dockerBuildContext, err := os.Open(dockerContextTarPath) dockerBuildContext, err := os.Open(dockerContextTarPath)
@ -282,6 +285,16 @@ func buildDockerImageContextTar(buildContext string) (string, error) {
return "", fmt.Errorf("unable to read build context - %v", err.Error()) return "", fmt.Errorf("unable to read build context - %v", err.Error())
} }
excludes, err := build.ReadDockerignore(buildContext)
if err != nil {
return "", fmt.Errorf("unable to read .dockerignore file - %v", err.Error())
}
pm, err := fileutils.NewPatternMatcher(excludes)
if err != nil {
return "", fmt.Errorf("unable to create pattern matcher from .dockerignore exlcudes - %v", err.Error())
}
tw := tar.NewWriter(tmpFile) tw := tar.NewWriter(tmpFile)
defer tw.Close() defer tw.Close()
@ -291,6 +304,46 @@ func buildDockerImageContextTar(buildContext string) (string, error) {
return err return err
} }
// if .dockerignore is present, ignore files from there
skip, err := pm.Matches(file)
if err != nil {
return err
}
// adapted from https://github.com/moby/moby/blob/v20.10.7/pkg/archive/archive.go#L851
if skip {
log.Printf("[DEBUG] Skipping file/dir from image build '%v'", file)
// If we want to skip this file and its a directory
// then we should first check to see if there's an
// excludes pattern (e.g. !dir/file) that starts with this
// dir. If so then we can't skip this dir.
// Its not a dir then so we can just return/skip.
if !info.IsDir() {
return nil
}
// No exceptions (!...) in patterns so just skip dir
if !pm.Exclusions() {
return filepath.SkipDir
}
dirSlash := file + string(filepath.Separator)
for _, pat := range pm.Patterns() {
if !pat.Exclusion() {
continue
}
if strings.HasPrefix(pat.String()+string(filepath.Separator), dirSlash) {
// found a match - so can't skip this dir
return nil
}
}
// No matching exclusion dir so just skip dir
return filepath.SkipDir
}
// create a new dir/file header // create a new dir/file header
header, err := tar.FileInfoHeader(info, info.Name()) header, err := tar.FileInfoHeader(info, info.Name())
if err != nil { if err != nil {

View file

@ -154,6 +154,59 @@ func TestAccDockerRegistryImageResource_buildAndKeep(t *testing.T) {
}) })
} }
func TestAccDockerRegistryImageResource_buildWithDockerignore(t *testing.T) {
pushOptions := createPushImageOptions("127.0.0.1:15000/tftest-dockerregistryimage-ignore:1.0")
wd, _ := os.Getwd()
context := strings.ReplaceAll((filepath.Join(wd, "..", "..", "scripts", "testing", "docker_registry_image_context_dockerignore")), "\\", "\\\\")
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(loadTestConfiguration(t, RESOURCE, "docker_registry_image", "testBuildDockerRegistryImageNoKeepConfig"), pushOptions.Registry, pushOptions.Name, context),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("docker_registry_image.foo", "sha256_digest"),
),
},
{
Config: fmt.Sprintf(loadTestConfiguration(t, RESOURCE, "docker_registry_image", "testBuildDockerRegistryImageNoKeepConfig"), pushOptions.Registry, pushOptions.Name, context),
Check: func(*terraform.State) error {
// we will modify the ignored file
f, err := os.OpenFile(context+"/empty_to_ignore", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return fmt.Errorf("failed to open file: %w", err)
}
defer f.Close()
_, err = f.WriteString("modify-me")
if err != nil {
return fmt.Errorf("failed to write to file: %w", err)
}
return nil
},
},
{
Config: fmt.Sprintf(loadTestConfiguration(t, RESOURCE, "docker_registry_image", "testBuildDockerRegistryImageNoKeepConfig"), pushOptions.Registry, pushOptions.Name, context),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("docker_registry_image.foo", "sha256_digest"),
),
},
},
CheckDestroy: resource.ComposeTestCheckFunc(
testDockerRegistryImageNotInRegistry(pushOptions),
func(*terraform.State) error {
// the 0 specifies the file will be empty afterwards
err := os.Truncate(context+"/empty_to_ignore", 0)
if err != nil {
return fmt.Errorf("failed to truncate the ignored file: %w", err)
}
return nil
},
),
})
}
func TestAccDockerRegistryImageResource_pushMissingImage(t *testing.T) { func TestAccDockerRegistryImageResource_pushMissingImage(t *testing.T) {
resource.Test(t, resource.TestCase{ resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) }, PreCheck: func() { testAccPreCheck(t) },

View file

@ -0,0 +1 @@
empty_to_ignore

View file

@ -0,0 +1,2 @@
FROM scratch
COPY empty /empty