diff --git a/pkg/build/build_phase.go b/pkg/build/build_phase.go index 29a8c5b9df..36802f5e3a 100644 --- a/pkg/build/build_phase.go +++ b/pkg/build/build_phase.go @@ -696,7 +696,7 @@ func (phase *BuildPhase) prepareStageInstructions(ctx context.Context, img *imag }) } else { stageImage.Builder.DockerfileStageBuilder().SetBuildContextArchive(phase.buildContextArchive) - stageImage.Builder.DockerfileStageBuilder().AppendPostInstruction(backend_instruction.NewLabel(*dockerfile_instruction.NewLabel(serviceLabels))) + stageImage.Builder.DockerfileStageBuilder().AppendPostInstruction(backend_instruction.NewLabel(*dockerfile_instruction.NewLabel("", serviceLabels))) } err := stg.PrepareImage(ctx, phase.Conveyor, phase.Conveyor.ContainerBackend, phase.StagesIterator.GetPrevBuiltImage(img, stg), stageImage, phase.buildContextArchive) diff --git a/pkg/build/image/build_context_archive.go b/pkg/build/image/build_context_archive.go index 3a26aeb9d7..b38b9bbb60 100644 --- a/pkg/build/image/build_context_archive.go +++ b/pkg/build/image/build_context_archive.go @@ -105,3 +105,14 @@ func (a *BuildContextArchive) CleanupExtractedDir(ctx context.Context) { logboek.Context(ctx).Warn().LogF("WARNING: unable to remove extracted context dir %q: %s", a.extractionDir, err) } } + +func (a *BuildContextArchive) CalculatePathsChecksum(ctx context.Context, paths []string) (string, error) { + dir, err := a.ExtractOrGetExtractedDir(ctx) + if err != nil { + return "", fmt.Errorf("unable to access context directory: %w", err) + } + + _ = dir + + return "", nil +} diff --git a/pkg/build/stage/dependencies_test.go b/pkg/build/stage/dependencies_test.go index 8d11821979..56574e3e44 100644 --- a/pkg/build/stage/dependencies_test.go +++ b/pkg/build/stage/dependencies_test.go @@ -16,7 +16,7 @@ var _ = Describe("DependenciesStage", func() { ctx := context.Background() conveyor := NewConveyorStubForDependencies(NewGiterminismManagerStub(NewLocalGitRepoStub("9d8059842b6fde712c58315ca0ab4713d90761c0"), NewGiterminismInspectorStub()), data.Dependencies) - containerBackend := NewContainerBackendMock() + containerBackend := NewContainerBackendStub() stage := newDependenciesStage(nil, GetConfigDependencies(data.Dependencies), "example-stage", &BaseStageOptions{ ImageName: "example-image", @@ -270,3 +270,15 @@ var _ = Describe("getDependencies helper", func() { }) }) }) + +func NewConveyorStubForDependencies(giterminismManager *GiterminismManagerStub, dependencies []*TestDependency) *ConveyorStub { + lastStageImageNameByImageName := make(map[string]string) + lastStageImageIDByImageName := make(map[string]string) + + for _, dep := range dependencies { + lastStageImageNameByImageName[dep.ImageName] = dep.GetDockerImageName() + lastStageImageIDByImageName[dep.ImageName] = dep.DockerImageID + } + + return NewConveyorStub(giterminismManager, lastStageImageNameByImageName, lastStageImageIDByImageName) +} diff --git a/pkg/build/stage/full_dockerfile.go b/pkg/build/stage/full_dockerfile.go index 4dfad22933..84ca1a8a2d 100644 --- a/pkg/build/stage/full_dockerfile.go +++ b/pkg/build/stage/full_dockerfile.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "os" - "path" "path/filepath" "strconv" "strings" @@ -21,6 +20,7 @@ import ( "github.com/werf/werf/pkg/container_backend/stage_builder" "github.com/werf/werf/pkg/context_manager" "github.com/werf/werf/pkg/docker_registry" + "github.com/werf/werf/pkg/dockerfile" "github.com/werf/werf/pkg/git_repo" "github.com/werf/werf/pkg/giterminism_manager" "github.com/werf/werf/pkg/image" @@ -758,7 +758,7 @@ func (s *FullDockerfileStage) calculateFilesChecksum(ctx context.Context, giterm var checksum string var err error - normalizedWildcards := normalizeCopyAddSources(wildcards) + normalizedWildcards := dockerfile.NormalizeCopyAddSourcesForPathMatcher(wildcards) logProcess := logboek.Context(ctx).Debug().LogProcess("Calculating files checksum (%v) from local git repo", normalizedWildcards) logProcess.Start() @@ -835,22 +835,6 @@ func (s *FullDockerfileStage) calculateFilesChecksumWithGit(ctx context.Context, return util.Sha256Hash(lsTreeResultChecksum), nil } -func normalizeCopyAddSources(wildcards []string) []string { - var result []string - for _, wildcard := range wildcards { - normalizedWildcard := path.Clean(wildcard) - if normalizedWildcard == "/" { - normalizedWildcard = "." - } else if strings.HasPrefix(normalizedWildcard, "/") { - normalizedWildcard = strings.TrimPrefix(normalizedWildcard, "/") - } - - result = append(result, normalizedWildcard) - } - - return result -} - func dockerfileStageDependenciesDebug() bool { return os.Getenv("WERF_DEBUG_DOCKERFILE_STAGE_DEPENDENCIES") == "1" } diff --git a/pkg/build/stage/full_dockerfile_test.go b/pkg/build/stage/full_dockerfile_test.go index 164a680c1e..1fee7016fb 100644 --- a/pkg/build/stage/full_dockerfile_test.go +++ b/pkg/build/stage/full_dockerfile_test.go @@ -65,7 +65,7 @@ var _ = Describe("FullDockerfileStage", func() { ctx := context.Background() conveyor := NewConveyorStubForDependencies(NewGiterminismManagerStub(NewLocalGitRepoStub("9d8059842b6fde712c58315ca0ab4713d90761c0"), NewGiterminismInspectorStub()), data.TestDependencies.Dependencies) - containerBackend := NewContainerBackendMock() + containerBackend := NewContainerBackendStub() dockerStages, dockerMetaArgs := testDockerfileToDockerStages(data.DockerfileData) @@ -295,7 +295,7 @@ RUN echo hello stage := newTestFullDockerfileStage(dockerfile, "", nil, dockerStages, dockerMetaArgs, nil) - containerBackend := NewContainerBackendMock() + containerBackend := NewContainerBackendStub() dockerRegistry := NewDockerRegistryApiStub() @@ -328,7 +328,7 @@ RUN --mount=type=bind,from=build,source=/usr/local/test_project/dist,target=/usr stage := newTestFullDockerfileStage(dockerfile, "", nil, dockerStages, dockerMetaArgs, nil) - containerBackend := NewContainerBackendMock() + containerBackend := NewContainerBackendStub() img := NewLegacyImageStub() stageBuilder := stage_builder.NewStageBuilder(containerBackend, "", img) diff --git a/pkg/build/stage/git_mapping_test.go b/pkg/build/stage/git_mapping_test.go index 1842d6d2f7..931d5c6e9b 100644 --- a/pkg/build/stage/git_mapping_test.go +++ b/pkg/build/stage/git_mapping_test.go @@ -127,7 +127,7 @@ var _ = Describe("GitMapping", func() { func(data BaseCommitForPrevBuiltImageCheckData, checkResultFunc func(string, BaseCommitForPrevBuiltImageCheckData)) { ctx := context.Background() c := NewConveyorStub(stage.VirtualMergeOptions{VirtualMerge: data.IsCurrentCommitVirtualMerge}) - containerBackend := stage.NewContainerBackendMock() + containerBackend := stage.NewContainerBackendStub() gitRepo := NewGitRepoStub("own", true, data.CurrentCommit) gitMapping.SetGitRepo(gitRepo) diff --git a/pkg/build/stage/instruction/add.go b/pkg/build/stage/instruction/add.go index e4ba22bccb..e503dc7bd4 100644 --- a/pkg/build/stage/instruction/add.go +++ b/pkg/build/stage/instruction/add.go @@ -2,6 +2,7 @@ package instruction import ( "context" + "fmt" "github.com/werf/werf/pkg/build/stage" "github.com/werf/werf/pkg/config" @@ -22,10 +23,24 @@ func NewAdd(name stage.StageName, i *dockerfile.DockerfileStageInstruction[*dock func (stage *Add) GetDependencies(ctx context.Context, c stage.Conveyor, cb container_backend.ContainerBackend, prevImage, prevBuiltImage *stage.StageImage, buildContextArchive container_backend.BuildContextArchiver) (string, error) { var args []string + args = append(args, stage.instruction.Data.Name()) + args = append(args, stage.instruction.Data.Raw) args = append(args, stage.instruction.Data.Src...) args = append(args, stage.instruction.Data.Dst) args = append(args, stage.instruction.Data.Chown) args = append(args, stage.instruction.Data.Chmod) + + // TODO(staged-dockerfile): support http src and --checksum option: https://docs.docker.com/engine/reference/builder/#verifying-a-remote-file-checksum-add---checksumchecksum-http-src-dest + // TODO(staged-dockerfile): support git ref: https://docs.docker.com/engine/reference/builder/#adding-a-git-repository-add-git-ref-dir + // TODO(staged-dockerfile): support --keep-git-dir for git: https://docs.docker.com/engine/reference/builder/#adding-a-git-repository-add-git-ref-dir + // TODO(staged-dockerfile): support --link + + pathsChecksum, err := buildContextArchive.CalculatePathsChecksum(ctx, stage.instruction.Data.Src) + if err != nil { + return "", fmt.Errorf("unable to calculate build context paths checksum: %w", err) + } + args = append(args, fmt.Sprintf("src-checksum=%s", pathsChecksum)) + return util.Sha256Hash(args...), nil } diff --git a/pkg/build/stage/instruction/add_test.go b/pkg/build/stage/instruction/add_test.go new file mode 100644 index 0000000000..6c75cf7938 --- /dev/null +++ b/pkg/build/stage/instruction/add_test.go @@ -0,0 +1,167 @@ +package instruction + +import ( + "context" + "fmt" + "strings" + + "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/werf/werf/pkg/build/stage" + "github.com/werf/werf/pkg/container_backend" + "github.com/werf/werf/pkg/dockerfile" + dockerfile_instruction "github.com/werf/werf/pkg/dockerfile/instruction" + "github.com/werf/werf/pkg/util" +) + +var ( + Entry = ginkgo.Entry + DescribeTable = ginkgo.DescribeTable +) + +var _ = DescribeTable("calculating digest and configuring builder", + func(data *TestData) { + ctx := context.Background() + + digest, err := data.Stage.GetDependencies(ctx, data.Conveyor, data.ContainerBackend, nil, data.StageImage, data.BuildContext) + Expect(err).To(Succeed()) + + fmt.Printf("calculated digest: %s\n", digest) + fmt.Printf("expected digest: %s\n", data.ExpectedDigest) + + Expect(digest).To(Equal(data.ExpectedDigest)) + }, + + Entry("ADD basic", NewTestData( + NewAdd("ADD", dockerfile.NewDockerfileStageInstruction( + dockerfile_instruction.NewAdd("", []string{"src"}, "/app", "1000:1000", "")), nil, false, + &stage.BaseStageOptions{ + ImageName: "example-image", + ProjectName: "example-project", + }, + ), + "bf369f0c1d046108f1a7314b086cefe109b8edc01878033eccf41f1ff47e2c73", + []*FileData{ + {Name: "src/main/java/worker/Worker.java", Data: []byte(`package worker;`)}, + {Name: "src/Worker/Program.cs", Data: []byte(`namespace Worker {}`)}, + }, + )), + + Entry("ADD with changed chown", NewTestData( + NewAdd("ADD", dockerfile.NewDockerfileStageInstruction( + dockerfile_instruction.NewAdd("", []string{"src"}, "/app", "1000:1001", "")), nil, false, + &stage.BaseStageOptions{ + ImageName: "example-image", + ProjectName: "example-project", + }, + ), + "2a9fc5cdddb5443ff19bcc6366a3ad42f2ac8e86a3779dadf0ac34857b3928cb", + []*FileData{ + {Name: "src/main/java/worker/Worker.java", Data: []byte(`package worker;`)}, + {Name: "src/Worker/Program.cs", Data: []byte(`namespace Worker {}`)}, + {Name: "pom.xml", Data: []byte(``)}, + }, + )), + + Entry("ADD with changed chmod", NewTestData( + NewAdd("ADD", dockerfile.NewDockerfileStageInstruction( + dockerfile_instruction.NewAdd("", []string{"src"}, "/app", "1000:1001", "0777")), nil, false, + &stage.BaseStageOptions{ + ImageName: "example-image", + ProjectName: "example-project", + }, + ), + "e2c17b3d62eb61470f16544a4ea42b66b37326dc5f10d61b7a9ebab04e53e7a7", + []*FileData{ + {Name: "src/main/java/worker/Worker.java", Data: []byte(`package worker;`)}, + {Name: "src/Worker/Program.cs", Data: []byte(`namespace Worker {}`)}, + {Name: "pom.xml", Data: []byte(``)}, + }, + )), + + Entry("ADD with changed sources paths", NewTestData( + NewAdd("ADD", dockerfile.NewDockerfileStageInstruction( + dockerfile_instruction.NewAdd("", []string{"src", "pom.xml"}, "/app", "1000:1000", "0777")), nil, false, + &stage.BaseStageOptions{ + ImageName: "example-image", + ProjectName: "example-project", + }, + ), + "cc635522d684dd86345d3a074722ecaf82fd33c33912c6667caea62177ba2540", + []*FileData{ + {Name: "src/main/java/worker/Worker.java", Data: []byte(`package worker;`)}, + {Name: "src/Worker/Program.cs", Data: []byte(`namespace Worker {}`)}, + {Name: "pom.xml", Data: []byte(``)}, + }, + )), + + Entry("ADD with changed source files", NewTestData( + NewAdd("ADD", dockerfile.NewDockerfileStageInstruction( + dockerfile_instruction.NewAdd("", []string{"src", "pom.xml"}, "/app", "1000:1000", "0777")), nil, false, + &stage.BaseStageOptions{ + ImageName: "example-image", + ProjectName: "example-project", + }, + ), + "15add63c912f20138ed0eb4a2bb906bc3c3fa70d0d0ac26c7871363484a84c94", + []*FileData{ + {Name: "src/main/java/worker/Worker.java", Data: []byte(`package worker2;`)}, + {Name: "src/Worker/Program.cs", Data: []byte(`namespace Worker2 {}`)}, + {Name: "pom.xml", Data: []byte(``)}, + }, + )), + + Entry("ADD with changed destination path", NewTestData( + NewAdd("ADD", dockerfile.NewDockerfileStageInstruction( + dockerfile_instruction.NewAdd("", []string{"src", "pom.xml"}, "/app2", "1000:1000", "0777")), nil, false, + &stage.BaseStageOptions{ + ImageName: "example-image", + ProjectName: "example-project", + }, + ), + "15b2f0b0d9b569a55f0af7c8326e2d5d8792f5a7df1f8fd4e612034f74a015e1", + []*FileData{ + {Name: "src/main/java/worker/Worker.java", Data: []byte(`package worker2;`)}, + {Name: "src/Worker/Program.cs", Data: []byte(`namespace Worker2 {}`)}, + {Name: "pom.xml", Data: []byte(``)}, + }, + )), +) + +type BuildContextStub struct { + container_backend.BuildContextArchiver + + Files []*FileData +} + +type FileData struct { + Name string + Data []byte +} + +func NewBuildContextStub(files []*FileData) *BuildContextStub { + return &BuildContextStub{Files: files} +} + +func (buildContext *BuildContextStub) CalculatePathsChecksum(ctx context.Context, paths []string) (string, error) { + var args []string + + for _, p := range paths { + for _, f := range buildContext.Files { + if f.Name == p { + args = append(args, string(f.Data)) + break + } + } + + for _, f := range buildContext.Files { + if strings.HasPrefix(f.Name, p) { + args = append(args, string(f.Data)) + break + } + } + } + + return util.Sha256Hash(args...), nil +} diff --git a/pkg/build/stage/instruction/stubs_test.go b/pkg/build/stage/instruction/stubs_test.go new file mode 100644 index 0000000000..694433866d --- /dev/null +++ b/pkg/build/stage/instruction/stubs_test.go @@ -0,0 +1,43 @@ +package instruction + +import ( + "github.com/werf/werf/pkg/build/stage" + "github.com/werf/werf/pkg/container_backend/stage_builder" +) + +type TestData struct { + Stage stage.Interface + ExpectedDigest string + + Conveyor *stage.ConveyorStub + ContainerBackend *stage.ContainerBackendStub + Image *stage.LegacyImageStub + StageBuilder *stage_builder.StageBuilder + StageImage *stage.StageImage + BuildContext *BuildContextStub +} + +func NewTestData(stg stage.Interface, expectedDigest string, files []*FileData) *TestData { + conveyor := stage.NewConveyorStub(stage.NewGiterminismManagerStub(stage.NewLocalGitRepoStub("9d8059842b6fde712c58315ca0ab4713d90761c0"), stage.NewGiterminismInspectorStub()), nil, nil) + containerBackend := stage.NewContainerBackendStub() + + img := stage.NewLegacyImageStub() + stageBuilder := stage_builder.NewStageBuilder(containerBackend, "", img) + stageImage := &stage.StageImage{ + Image: img, + Builder: stageBuilder, + } + + buildContext := NewBuildContextStub(files) + + return &TestData{ + Stage: stg, + ExpectedDigest: expectedDigest, + Conveyor: conveyor, + ContainerBackend: containerBackend, + Image: img, + StageBuilder: stageBuilder, + StageImage: stageImage, + BuildContext: buildContext, + } +} diff --git a/pkg/build/stage/instruction/suite_test.go b/pkg/build/stage/instruction/suite_test.go new file mode 100644 index 0000000000..d8dabe56c2 --- /dev/null +++ b/pkg/build/stage/instruction/suite_test.go @@ -0,0 +1,13 @@ +package instruction + +import ( + "testing" + + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" +) + +func TestStage(t *testing.T) { + gomega.RegisterFailHandler(ginkgo.Fail) + ginkgo.RunSpecs(t, "Dockerfile Instruction Suite") +} diff --git a/pkg/build/stage/stubs_test.go b/pkg/build/stage/stubs.go similarity index 86% rename from pkg/build/stage/stubs_test.go rename to pkg/build/stage/stubs.go index 1f4f1cc268..cbc8400879 100644 --- a/pkg/build/stage/stubs_test.go +++ b/pkg/build/stage/stubs.go @@ -78,18 +78,6 @@ func NewConveyorStub(giterminismManager *GiterminismManagerStub, lastStageImageN } } -func NewConveyorStubForDependencies(giterminismManager *GiterminismManagerStub, dependencies []*TestDependency) *ConveyorStub { - lastStageImageNameByImageName := make(map[string]string) - lastStageImageIDByImageName := make(map[string]string) - - for _, dep := range dependencies { - lastStageImageNameByImageName[dep.ImageName] = dep.GetDockerImageName() - lastStageImageIDByImageName[dep.ImageName] = dep.DockerImageID - } - - return NewConveyorStub(giterminismManager, lastStageImageNameByImageName, lastStageImageIDByImageName) -} - func (c *ConveyorStub) UseLegacyStapelBuilder(cr container_backend.ContainerBackend) bool { return true } @@ -190,27 +178,27 @@ func (archive *GitRepoArchiveStub) GetFilePath() string { return "no-such-file" } -type ContainerBackendMock struct { +type ContainerBackendStub struct { container_backend.ContainerBackend _PulledImages map[string]bool } -func NewContainerBackendMock() *ContainerBackendMock { - return &ContainerBackendMock{ +func NewContainerBackendStub() *ContainerBackendStub { + return &ContainerBackendStub{ _PulledImages: make(map[string]bool), } } -func (containerBackend *ContainerBackendMock) HasContainerRootMountSupport() bool { +func (containerBackend *ContainerBackendStub) HasContainerRootMountSupport() bool { return false } -func (containerBackend *ContainerBackendMock) GetImageInfo(ctx context.Context, ref string, opts container_backend.GetImageInfoOpts) (*image.Info, error) { +func (containerBackend *ContainerBackendStub) GetImageInfo(ctx context.Context, ref string, opts container_backend.GetImageInfoOpts) (*image.Info, error) { return nil, nil } -func (containerBackend *ContainerBackendMock) Pull(ctx context.Context, ref string, opts container_backend.PullOpts) error { +func (containerBackend *ContainerBackendStub) Pull(ctx context.Context, ref string, opts container_backend.PullOpts) error { containerBackend._PulledImages[ref] = true return nil } diff --git a/pkg/container_backend/archive_interface.go b/pkg/container_backend/archive_interface.go index 7e1cf9ae6d..1bbc2af7d9 100644 --- a/pkg/container_backend/archive_interface.go +++ b/pkg/container_backend/archive_interface.go @@ -6,6 +6,7 @@ type BuildContextArchiver interface { Create(ctx context.Context, opts BuildContextArchiveCreateOptions) error Path() string ExtractOrGetExtractedDir(ctx context.Context) (string, error) + CalculatePathsChecksum(ctx context.Context, paths []string) (string, error) CleanupExtractedDir(ctx context.Context) } diff --git a/pkg/dockerfile/frontend/buildkit_dockerfile.go b/pkg/dockerfile/frontend/buildkit_dockerfile.go index ca99eaf17b..d1bb973cbf 100644 --- a/pkg/dockerfile/frontend/buildkit_dockerfile.go +++ b/pkg/dockerfile/frontend/buildkit_dockerfile.go @@ -54,43 +54,43 @@ func DockerfileStageFromBuildkitStage(index int, stage instructions.Stage) *dock switch typedCmd := cmd.(type) { case *instructions.AddCommand: src, dst := extractSrcAndDst(typedCmd.SourcesAndDest) - i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewAdd(src, dst, typedCmd.Chown, typedCmd.Chmod))) + i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewAdd(typedCmd.String(), src, dst, typedCmd.Chown, typedCmd.Chmod))) case *instructions.ArgCommand: - i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewArg(typedCmd.Args))) + i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewArg(typedCmd.String(), typedCmd.Args))) case *instructions.CmdCommand: - i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewCmd(typedCmd.CmdLine, typedCmd.PrependShell))) + i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewCmd(typedCmd.String(), typedCmd.CmdLine, typedCmd.PrependShell))) case *instructions.CopyCommand: src, dst := extractSrcAndDst(typedCmd.SourcesAndDest) - i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewCopy(typedCmd.From, src, dst, typedCmd.Chown, typedCmd.Chmod))) + i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewCopy(typedCmd.String(), typedCmd.From, src, dst, typedCmd.Chown, typedCmd.Chmod))) case *instructions.EntrypointCommand: - i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewEntrypoint(typedCmd.CmdLine, typedCmd.PrependShell))) + i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewEntrypoint(typedCmd.String(), typedCmd.CmdLine, typedCmd.PrependShell))) case *instructions.EnvCommand: - i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewEnv(extractKeyValuePairsAsMap(typedCmd.Env)))) + i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewEnv(typedCmd.String(), extractKeyValuePairsAsMap(typedCmd.Env)))) case *instructions.ExposeCommand: - i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewExpose(typedCmd.Ports))) + i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewExpose(typedCmd.String(), typedCmd.Ports))) case *instructions.HealthCheckCommand: - i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewHealthcheck(typedCmd.Health))) + i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewHealthcheck(typedCmd.String(), typedCmd.Health))) case *instructions.LabelCommand: - i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewLabel(extractKeyValuePairsAsMap(typedCmd.Labels)))) + i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewLabel(typedCmd.String(), extractKeyValuePairsAsMap(typedCmd.Labels)))) case *instructions.MaintainerCommand: - i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewMaintainer(typedCmd.Maintainer))) + i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewMaintainer(typedCmd.String(), typedCmd.Maintainer))) case *instructions.OnbuildCommand: - i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewOnBuild(typedCmd.Expression))) + i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewOnBuild(typedCmd.String(), typedCmd.Expression))) case *instructions.RunCommand: network := dockerfile_instruction.NewNetworkType(instructions.GetNetwork(typedCmd)) security := dockerfile_instruction.NewSecurityType(instructions.GetSecurity(typedCmd)) mounts := instructions.GetMounts(typedCmd) - i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewRun(typedCmd.CmdLine, typedCmd.PrependShell, mounts, network, security))) + i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewRun(typedCmd.String(), typedCmd.CmdLine, typedCmd.PrependShell, mounts, network, security))) case *instructions.ShellCommand: - i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewShell(typedCmd.Shell))) + i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewShell(typedCmd.String(), typedCmd.Shell))) case *instructions.StopSignalCommand: - i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewStopSignal(typedCmd.Signal))) + i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewStopSignal(typedCmd.String(), typedCmd.Signal))) case *instructions.UserCommand: - i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewUser(typedCmd.User))) + i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewUser(typedCmd.String(), typedCmd.User))) case *instructions.VolumeCommand: - i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewVolume(typedCmd.Volumes))) + i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewVolume(typedCmd.String(), typedCmd.Volumes))) case *instructions.WorkdirCommand: - i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewWorkdir(typedCmd.Path))) + i = append(i, dockerfile.NewDockerfileStageInstruction(dockerfile_instruction.NewWorkdir(typedCmd.String(), typedCmd.Path))) } } diff --git a/pkg/dockerfile/helpers.go b/pkg/dockerfile/helpers.go new file mode 100644 index 0000000000..96aba60b11 --- /dev/null +++ b/pkg/dockerfile/helpers.go @@ -0,0 +1,21 @@ +package dockerfile + +import ( + "path" + "strings" +) + +func NormalizeCopyAddSourcesForPathMatcher(wildcards []string) []string { + var result []string + for _, wildcard := range wildcards { + normalizedWildcard := path.Clean(wildcard) + if normalizedWildcard == "/" { + normalizedWildcard = "." + } + normalizedWildcard = strings.TrimPrefix(normalizedWildcard, "/") + + result = append(result, normalizedWildcard) + } + + return result +} diff --git a/pkg/dockerfile/instruction/add.go b/pkg/dockerfile/instruction/add.go index 62d8e0362a..8882a125b4 100644 --- a/pkg/dockerfile/instruction/add.go +++ b/pkg/dockerfile/instruction/add.go @@ -1,14 +1,16 @@ package instruction type Add struct { + *Base + Src []string Dst string Chown string Chmod string } -func NewAdd(src []string, dst, chown, chmod string) *Add { - return &Add{Src: src, Dst: dst, Chown: chown, Chmod: chmod} +func NewAdd(raw string, src []string, dst, chown, chmod string) *Add { + return &Add{Base: NewBase(raw), Src: src, Dst: dst, Chown: chown, Chmod: chmod} } func (i *Add) Name() string { diff --git a/pkg/dockerfile/instruction/arg.go b/pkg/dockerfile/instruction/arg.go index 58b052f951..95ff7a6ccc 100644 --- a/pkg/dockerfile/instruction/arg.go +++ b/pkg/dockerfile/instruction/arg.go @@ -3,11 +3,13 @@ package instruction import "github.com/moby/buildkit/frontend/dockerfile/instructions" type Arg struct { + *Base + Args []instructions.KeyValuePairOptional } -func NewArg(args []instructions.KeyValuePairOptional) *Arg { - return &Arg{Args: args} +func NewArg(raw string, args []instructions.KeyValuePairOptional) *Arg { + return &Arg{Base: NewBase(raw), Args: args} } func (i *Arg) Name() string { diff --git a/pkg/dockerfile/instruction/base.go b/pkg/dockerfile/instruction/base.go new file mode 100644 index 0000000000..08c1365a96 --- /dev/null +++ b/pkg/dockerfile/instruction/base.go @@ -0,0 +1,9 @@ +package instruction + +type Base struct { + Raw string +} + +func NewBase(raw string) *Base { + return &Base{Raw: raw} +} diff --git a/pkg/dockerfile/instruction/cmd.go b/pkg/dockerfile/instruction/cmd.go index 1cc4d21b50..916b65d267 100644 --- a/pkg/dockerfile/instruction/cmd.go +++ b/pkg/dockerfile/instruction/cmd.go @@ -1,12 +1,14 @@ package instruction type Cmd struct { + *Base + Cmd []string PrependShell bool } -func NewCmd(cmd []string, prependShell bool) *Cmd { - return &Cmd{Cmd: cmd, PrependShell: prependShell} +func NewCmd(raw string, cmd []string, prependShell bool) *Cmd { + return &Cmd{Base: NewBase(raw), Cmd: cmd, PrependShell: prependShell} } func (i *Cmd) Name() string { diff --git a/pkg/dockerfile/instruction/copy.go b/pkg/dockerfile/instruction/copy.go index 5a3ac4837c..2f6dc8ca91 100644 --- a/pkg/dockerfile/instruction/copy.go +++ b/pkg/dockerfile/instruction/copy.go @@ -1,6 +1,8 @@ package instruction type Copy struct { + *Base + From string Src []string Dst string @@ -8,8 +10,8 @@ type Copy struct { Chmod string } -func NewCopy(from string, src []string, dst, chown, chmod string) *Copy { - return &Copy{From: from, Src: src, Dst: dst, Chown: chown, Chmod: chmod} +func NewCopy(raw, from string, src []string, dst, chown, chmod string) *Copy { + return &Copy{Base: NewBase(raw), From: from, Src: src, Dst: dst, Chown: chown, Chmod: chmod} } func (i *Copy) Name() string { diff --git a/pkg/dockerfile/instruction/entrypoint.go b/pkg/dockerfile/instruction/entrypoint.go index f19f775cd9..c7fa255c24 100644 --- a/pkg/dockerfile/instruction/entrypoint.go +++ b/pkg/dockerfile/instruction/entrypoint.go @@ -1,12 +1,14 @@ package instruction type Entrypoint struct { + *Base + Entrypoint []string PrependShell bool } -func NewEntrypoint(entrypoint []string, prependShell bool) *Entrypoint { - return &Entrypoint{Entrypoint: entrypoint, PrependShell: prependShell} +func NewEntrypoint(raw string, entrypoint []string, prependShell bool) *Entrypoint { + return &Entrypoint{Base: NewBase(raw), Entrypoint: entrypoint, PrependShell: prependShell} } func (i *Entrypoint) Name() string { diff --git a/pkg/dockerfile/instruction/env.go b/pkg/dockerfile/instruction/env.go index ebd4daa819..6a1c245499 100644 --- a/pkg/dockerfile/instruction/env.go +++ b/pkg/dockerfile/instruction/env.go @@ -1,11 +1,13 @@ package instruction type Env struct { + *Base + Envs map[string]string } -func NewEnv(envs map[string]string) *Env { - return &Env{Envs: envs} +func NewEnv(raw string, envs map[string]string) *Env { + return &Env{Base: NewBase(raw), Envs: envs} } func (i *Env) Name() string { diff --git a/pkg/dockerfile/instruction/expose.go b/pkg/dockerfile/instruction/expose.go index 8d452eda08..abf973b749 100644 --- a/pkg/dockerfile/instruction/expose.go +++ b/pkg/dockerfile/instruction/expose.go @@ -1,11 +1,13 @@ package instruction type Expose struct { + *Base + Ports []string } -func NewExpose(ports []string) *Expose { - return &Expose{Ports: ports} +func NewExpose(raw string, ports []string) *Expose { + return &Expose{Base: NewBase(raw), Ports: ports} } func (i *Expose) Name() string { diff --git a/pkg/dockerfile/instruction/healthcheck.go b/pkg/dockerfile/instruction/healthcheck.go index 29e591cde3..3121bc2886 100644 --- a/pkg/dockerfile/instruction/healthcheck.go +++ b/pkg/dockerfile/instruction/healthcheck.go @@ -3,6 +3,8 @@ package instruction import "github.com/docker/docker/api/types/container" type Healthcheck struct { + *Base + // TODO(ilya-lesikov): isn't this should be a part of a Config.Test? Type HealthcheckType Config *container.HealthConfig @@ -25,8 +27,8 @@ func NewHealthcheckType(cfg *container.HealthConfig) HealthcheckType { } } -func NewHealthcheck(cfg *container.HealthConfig) *Healthcheck { - return &Healthcheck{Type: NewHealthcheckType(cfg), Config: cfg} +func NewHealthcheck(raw string, cfg *container.HealthConfig) *Healthcheck { + return &Healthcheck{Base: NewBase(raw), Type: NewHealthcheckType(cfg), Config: cfg} } func (i *Healthcheck) Name() string { diff --git a/pkg/dockerfile/instruction/label.go b/pkg/dockerfile/instruction/label.go index 375fedfaa3..2e0e4292a8 100644 --- a/pkg/dockerfile/instruction/label.go +++ b/pkg/dockerfile/instruction/label.go @@ -1,11 +1,13 @@ package instruction type Label struct { + *Base + Labels map[string]string } -func NewLabel(labels map[string]string) *Label { - return &Label{Labels: labels} +func NewLabel(raw string, labels map[string]string) *Label { + return &Label{Base: NewBase(raw), Labels: labels} } func (i *Label) Name() string { diff --git a/pkg/dockerfile/instruction/maintainer.go b/pkg/dockerfile/instruction/maintainer.go index e3ea39239d..1a2b7ab3a4 100644 --- a/pkg/dockerfile/instruction/maintainer.go +++ b/pkg/dockerfile/instruction/maintainer.go @@ -1,11 +1,13 @@ package instruction type Maintainer struct { + *Base + Maintainer string } -func NewMaintainer(maintainer string) *Maintainer { - return &Maintainer{Maintainer: maintainer} +func NewMaintainer(raw, maintainer string) *Maintainer { + return &Maintainer{Base: NewBase(raw), Maintainer: maintainer} } func (i *Maintainer) Name() string { diff --git a/pkg/dockerfile/instruction/on_build.go b/pkg/dockerfile/instruction/on_build.go index cf674938ca..7c4b56c5c3 100644 --- a/pkg/dockerfile/instruction/on_build.go +++ b/pkg/dockerfile/instruction/on_build.go @@ -1,11 +1,13 @@ package instruction type OnBuild struct { + *Base + Instruction string } -func NewOnBuild(instruction string) *OnBuild { - return &OnBuild{Instruction: instruction} +func NewOnBuild(raw, instruction string) *OnBuild { + return &OnBuild{Base: NewBase(raw), Instruction: instruction} } func (i *OnBuild) Name() string { diff --git a/pkg/dockerfile/instruction/run.go b/pkg/dockerfile/instruction/run.go index b88831adc2..e63e06b1fa 100644 --- a/pkg/dockerfile/instruction/run.go +++ b/pkg/dockerfile/instruction/run.go @@ -42,6 +42,8 @@ func NewSecurityType(security string) SecurityType { } type Run struct { + *Base + Command []string PrependShell bool Mounts []*instructions.Mount @@ -49,8 +51,8 @@ type Run struct { Security SecurityType } -func NewRun(command []string, prependShell bool, mounts []*instructions.Mount, network NetworkType, security SecurityType) *Run { - return &Run{Command: command, PrependShell: prependShell, Mounts: mounts, Network: network} +func NewRun(raw string, command []string, prependShell bool, mounts []*instructions.Mount, network NetworkType, security SecurityType) *Run { + return &Run{Base: NewBase(raw), Command: command, PrependShell: prependShell, Mounts: mounts, Network: network} } func (i *Run) Name() string { diff --git a/pkg/dockerfile/instruction/shell.go b/pkg/dockerfile/instruction/shell.go index 4283120582..21910c741f 100644 --- a/pkg/dockerfile/instruction/shell.go +++ b/pkg/dockerfile/instruction/shell.go @@ -1,11 +1,13 @@ package instruction type Shell struct { + *Base + Shell []string } -func NewShell(shell []string) *Shell { - return &Shell{Shell: shell} +func NewShell(raw string, shell []string) *Shell { + return &Shell{Base: NewBase(raw), Shell: shell} } func (i *Shell) Name() string { diff --git a/pkg/dockerfile/instruction/stop_signal.go b/pkg/dockerfile/instruction/stop_signal.go index 00c2dc40b6..80dd938338 100644 --- a/pkg/dockerfile/instruction/stop_signal.go +++ b/pkg/dockerfile/instruction/stop_signal.go @@ -1,11 +1,13 @@ package instruction type StopSignal struct { + *Base + Signal string } -func NewStopSignal(signal string) *StopSignal { - return &StopSignal{Signal: signal} +func NewStopSignal(raw, signal string) *StopSignal { + return &StopSignal{Base: NewBase(raw), Signal: signal} } func (i *StopSignal) Name() string { diff --git a/pkg/dockerfile/instruction/user.go b/pkg/dockerfile/instruction/user.go index 225dabf65b..6b2ff0bd5b 100644 --- a/pkg/dockerfile/instruction/user.go +++ b/pkg/dockerfile/instruction/user.go @@ -1,11 +1,13 @@ package instruction type User struct { + *Base + User string } -func NewUser(user string) *User { - return &User{User: user} +func NewUser(raw, user string) *User { + return &User{Base: NewBase(raw), User: user} } func (i *User) Name() string { diff --git a/pkg/dockerfile/instruction/volume.go b/pkg/dockerfile/instruction/volume.go index ae20ef4d78..7775361bd9 100644 --- a/pkg/dockerfile/instruction/volume.go +++ b/pkg/dockerfile/instruction/volume.go @@ -1,11 +1,13 @@ package instruction type Volume struct { + *Base + Volumes []string } -func NewVolume(volumes []string) *Volume { - return &Volume{Volumes: volumes} +func NewVolume(raw string, volumes []string) *Volume { + return &Volume{Base: NewBase(raw), Volumes: volumes} } func (i *Volume) Name() string { diff --git a/pkg/dockerfile/instruction/workdir.go b/pkg/dockerfile/instruction/workdir.go index a6dfed494f..c147d62ec5 100644 --- a/pkg/dockerfile/instruction/workdir.go +++ b/pkg/dockerfile/instruction/workdir.go @@ -1,11 +1,13 @@ package instruction type Workdir struct { + *Base + Workdir string } -func NewWorkdir(workdir string) *Workdir { - return &Workdir{Workdir: workdir} +func NewWorkdir(raw, workdir string) *Workdir { + return &Workdir{Base: NewBase(raw), Workdir: workdir} } func (i *Workdir) Name() string {