diff --git a/internal/provider/resource_docker_registry_image_funcs.go b/internal/provider/resource_docker_registry_image_funcs.go index f1fd9a66..a007370d 100644 --- a/internal/provider/resource_docker_registry_image_funcs.go +++ b/internal/provider/resource_docker_registry_image_funcs.go @@ -20,9 +20,11 @@ import ( "strings" "time" + "github.com/docker/cli/cli/command/image/build" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/client" + "github.com/docker/docker/pkg/fileutils" "github.com/docker/go-units" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "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) { buildContext = buildContext[:lastIndex] } + dockerContextTarPath, err := buildDockerImageContextTar(buildContext) 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) 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()) } + 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) defer tw.Close() @@ -291,6 +304,46 @@ func buildDockerImageContextTar(buildContext string) (string, error) { 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 header, err := tar.FileInfoHeader(info, info.Name()) if err != nil { diff --git a/internal/provider/resource_docker_registry_image_test.go b/internal/provider/resource_docker_registry_image_test.go index 15c47494..b1b6a263 100644 --- a/internal/provider/resource_docker_registry_image_test.go +++ b/internal/provider/resource_docker_registry_image_test.go @@ -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) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, diff --git a/scripts/testing/docker_registry_image_context_dockerignore/.dockerignore b/scripts/testing/docker_registry_image_context_dockerignore/.dockerignore new file mode 100644 index 00000000..90fd1815 --- /dev/null +++ b/scripts/testing/docker_registry_image_context_dockerignore/.dockerignore @@ -0,0 +1 @@ +empty_to_ignore \ No newline at end of file diff --git a/scripts/testing/docker_registry_image_context_dockerignore/Dockerfile b/scripts/testing/docker_registry_image_context_dockerignore/Dockerfile new file mode 100644 index 00000000..ef5380d1 --- /dev/null +++ b/scripts/testing/docker_registry_image_context_dockerignore/Dockerfile @@ -0,0 +1,2 @@ +FROM scratch +COPY empty /empty \ No newline at end of file diff --git a/scripts/testing/docker_registry_image_context_dockerignore/empty b/scripts/testing/docker_registry_image_context_dockerignore/empty new file mode 100644 index 00000000..e69de29b diff --git a/scripts/testing/docker_registry_image_context_dockerignore/empty_to_ignore b/scripts/testing/docker_registry_image_context_dockerignore/empty_to_ignore new file mode 100644 index 00000000..e69de29b