diff --git a/cmd/werf/cleanup/cleanup.go b/cmd/werf/cleanup/cleanup.go index 9910fcb6d7..07eb0389ff 100644 --- a/cmd/werf/cleanup/cleanup.go +++ b/cmd/werf/cleanup/cleanup.go @@ -240,7 +240,7 @@ It is worth noting that auto-cleaning is enabled by default, and manual use is u cleanupOptions := cleaning.CleanupOptions{ ImageNameList: imagesNames, - LocalGit: giterminismManager.LocalGitRepo(), + LocalGit: giterminismManager.LocalGitRepo().(*git_repo.Local), KubernetesContextClients: kubernetesContextClients, KubernetesNamespaceRestrictionByContext: common.GetKubernetesNamespaceRestrictionByContext(&commonCmdData, kubernetesContextClients), WithoutKube: *commonCmdData.WithoutKube, diff --git a/cmd/werf/common/common.go b/cmd/werf/common/common.go index 46ab82707f..589a2d5fa0 100644 --- a/cmd/werf/common/common.go +++ b/cmd/werf/common/common.go @@ -1134,8 +1134,8 @@ func GetCustomWerfConfigRelPath(giterminismManager giterminism_manager.Interface } customConfigPath = util.GetAbsoluteFilepath(customConfigPath) - if !util.IsSubpathOfBasePath(giterminismManager.LocalGitRepo().WorkTreeDir, customConfigPath) { - return "", fmt.Errorf("the werf config %q must be in the project git work tree %q", customConfigPath, giterminismManager.LocalGitRepo().WorkTreeDir) + if !util.IsSubpathOfBasePath(giterminismManager.LocalGitRepo().GetWorkTreeDir(), customConfigPath) { + return "", fmt.Errorf("the werf config %q must be in the project git work tree %q", customConfigPath, giterminismManager.LocalGitRepo().GetWorkTreeDir()) } return util.GetRelativeToBaseFilepath(giterminismManager.ProjectDir(), customConfigPath), nil @@ -1148,8 +1148,8 @@ func GetCustomWerfConfigTemplatesDirRelPath(giterminismManager giterminism_manag } customConfigTemplatesDirPath = util.GetAbsoluteFilepath(customConfigTemplatesDirPath) - if !util.IsSubpathOfBasePath(giterminismManager.LocalGitRepo().WorkTreeDir, customConfigTemplatesDirPath) { - return "", fmt.Errorf("the werf configuration templates directory %q must be in the project git work tree %q", customConfigTemplatesDirPath, giterminismManager.LocalGitRepo().WorkTreeDir) + if !util.IsSubpathOfBasePath(giterminismManager.LocalGitRepo().GetWorkTreeDir(), customConfigTemplatesDirPath) { + return "", fmt.Errorf("the werf configuration templates directory %q must be in the project git work tree %q", customConfigTemplatesDirPath, giterminismManager.LocalGitRepo().GetWorkTreeDir()) } return util.GetRelativeToBaseFilepath(giterminismManager.ProjectDir(), customConfigTemplatesDirPath), nil @@ -1240,8 +1240,8 @@ func GetHelmChartDir(werfConfigPath string, werfConfig *config.WerfConfig, giter } absHelmChartDir := filepath.Join(giterminismManager.ProjectDir(), helmChartDir) - if !util.IsSubpathOfBasePath(giterminismManager.LocalGitRepo().WorkTreeDir, absHelmChartDir) { - return "", fmt.Errorf("the chart directory %s must be in the project git work tree %s", absHelmChartDir, giterminismManager.LocalGitRepo().WorkTreeDir) + if !util.IsSubpathOfBasePath(giterminismManager.LocalGitRepo().GetWorkTreeDir(), absHelmChartDir) { + return "", fmt.Errorf("the chart directory %s must be in the project git work tree %s", absHelmChartDir, giterminismManager.LocalGitRepo().GetWorkTreeDir()) } return helmChartDir, nil diff --git a/pkg/build/conveyor.go b/pkg/build/conveyor.go index adc242bbd1..80f7c0d7d0 100644 --- a/pkg/build/conveyor.go +++ b/pkg/build/conveyor.go @@ -9,7 +9,6 @@ import ( "path" "path/filepath" "reflect" - "strconv" "strings" "sync" @@ -21,6 +20,7 @@ import ( "github.com/werf/logboek" stylePkg "github.com/werf/logboek/pkg/style" "github.com/werf/logboek/pkg/types" + "github.com/werf/werf/pkg/build/dockerfile_helpers" "github.com/werf/werf/pkg/build/import_server" "github.com/werf/werf/pkg/build/stage" "github.com/werf/werf/pkg/config" @@ -1257,33 +1257,19 @@ func prepareImageBasedOnImageFromDockerfile(ctx context.Context, imageFromDocker return nil, err } - resolveDockerStagesFromValue(dockerStages) + dockerfile_helpers.ResolveDockerStagesFromValue(dockerStages) - dockerTargetIndex, err := getDockerTargetStageIndex(dockerStages, imageFromDockerfileConfig.Target) + dockerTargetIndex, err := dockerfile_helpers.GetDockerTargetStageIndex(dockerStages, imageFromDockerfileConfig.Target) if err != nil { return nil, err } - dockerTargetStage := dockerStages[dockerTargetIndex] - - ds, err := stage.NewDockerStages( + ds := stage.NewDockerStages( dockerStages, util.MapStringInterfaceToMapStringString(imageFromDockerfileConfig.Args), dockerMetaArgs, dockerTargetIndex, ) - if err != nil { - return nil, err - } - - resolvedBaseName, err := ds.ShlexProcessWordWithMetaArgs(dockerTargetStage.BaseName) - if err != nil { - return nil, err - } - - if err := handleImageFromName(ctx, resolvedBaseName, false, img, c); err != nil { - return nil, err - } baseStageOptions := &stage.NewBaseStageOptions{ ImageName: imageFromDockerfileConfig.Name, @@ -1305,6 +1291,7 @@ func prepareImageBasedOnImageFromDockerfile(ctx context.Context, imageFromDocker ds, stage.NewContextChecksum(dockerignorePathMatcher), baseStageOptions, + imageFromDockerfileConfig.Dependencies, ) img.stages = append(img.stages, dockerfileStage) @@ -1313,38 +1300,3 @@ func prepareImageBasedOnImageFromDockerfile(ctx context.Context, imageFromDocker return img, nil } - -func resolveDockerStagesFromValue(stages []instructions.Stage) { - nameToIndex := make(map[string]string) - for i, s := range stages { - name := strings.ToLower(s.Name) - index := strconv.Itoa(i) - if name != index { - nameToIndex[name] = index - } - - for _, cmd := range s.Commands { - copyCmd, ok := cmd.(*instructions.CopyCommand) - if ok && copyCmd.From != "" { - from := strings.ToLower(copyCmd.From) - if val, ok := nameToIndex[from]; ok { - copyCmd.From = val - } - } - } - } -} - -func getDockerTargetStageIndex(dockerStages []instructions.Stage, dockerTargetStage string) (int, error) { - if dockerTargetStage == "" { - return len(dockerStages) - 1, nil - } - - for i, s := range dockerStages { - if s.Name == dockerTargetStage { - return i, nil - } - } - - return -1, fmt.Errorf("%s is not a valid target build stage", dockerTargetStage) -} diff --git a/pkg/build/dockerfile_helpers/dockerfile_helpers.go b/pkg/build/dockerfile_helpers/dockerfile_helpers.go new file mode 100644 index 0000000000..273167470d --- /dev/null +++ b/pkg/build/dockerfile_helpers/dockerfile_helpers.go @@ -0,0 +1,44 @@ +package dockerfile_helpers + +import ( + "fmt" + "strconv" + "strings" + + "github.com/moby/buildkit/frontend/dockerfile/instructions" +) + +func ResolveDockerStagesFromValue(stages []instructions.Stage) { + nameToIndex := make(map[string]string) + for i, s := range stages { + name := strings.ToLower(s.Name) + index := strconv.Itoa(i) + if name != index { + nameToIndex[name] = index + } + + for _, cmd := range s.Commands { + copyCmd, ok := cmd.(*instructions.CopyCommand) + if ok && copyCmd.From != "" { + from := strings.ToLower(copyCmd.From) + if val, ok := nameToIndex[from]; ok { + copyCmd.From = val + } + } + } + } +} + +func GetDockerTargetStageIndex(dockerStages []instructions.Stage, dockerTargetStage string) (int, error) { + if dockerTargetStage == "" { + return len(dockerStages) - 1, nil + } + + for i, s := range dockerStages { + if s.Name == dockerTargetStage { + return i, nil + } + } + + return -1, fmt.Errorf("%s is not a valid target build stage", dockerTargetStage) +} diff --git a/pkg/build/stage/data_test.go b/pkg/build/stage/data_test.go new file mode 100644 index 0000000000..576a90a11c --- /dev/null +++ b/pkg/build/stage/data_test.go @@ -0,0 +1,131 @@ +package stage + +import ( + "fmt" + + . "github.com/onsi/gomega" + + "github.com/werf/werf/pkg/config" + "github.com/werf/werf/pkg/util" +) + +type TestDependencies struct { + Dependencies []*TestDependency + ExpectedDigest string +} + +type TestDependency struct { + ImageName string + + TargetEnvImageName string + TargetEnvImageRepo string + TargetEnvImageTag string + TargetEnvImageID string + + TargetBuildArgImageName string + TargetBuildArgImageRepo string + TargetBuildArgImageTag string + TargetBuildArgImageID string + + DockerImageRepo string + DockerImageTag string + DockerImageID string +} + +func (dep *TestDependency) GetDockerImageName() string { + return fmt.Sprintf("%s:%s", dep.DockerImageRepo, dep.DockerImageTag) +} + +func (dep *TestDependency) ToConfigDependency() *config.Dependency { + depCfg := &config.Dependency{ImageName: dep.ImageName} + + if dep.TargetEnvImageName != "" { + depCfg.Imports = append(depCfg.Imports, &config.DependencyImport{ + Type: config.ImageNameImport, + TargetEnv: dep.TargetEnvImageName, + }) + } + if dep.TargetEnvImageRepo != "" { + depCfg.Imports = append(depCfg.Imports, &config.DependencyImport{ + Type: config.ImageRepoImport, + TargetEnv: dep.TargetEnvImageRepo, + }) + } + if dep.TargetEnvImageTag != "" { + depCfg.Imports = append(depCfg.Imports, &config.DependencyImport{ + Type: config.ImageTagImport, + TargetEnv: dep.TargetEnvImageTag, + }) + } + if dep.TargetEnvImageID != "" { + depCfg.Imports = append(depCfg.Imports, &config.DependencyImport{ + Type: config.ImageIDImport, + TargetEnv: dep.TargetEnvImageID, + }) + } + + if dep.TargetBuildArgImageName != "" { + depCfg.Imports = append(depCfg.Imports, &config.DependencyImport{ + Type: config.ImageNameImport, + TargetBuildArg: dep.TargetBuildArgImageName, + }) + } + if dep.TargetBuildArgImageRepo != "" { + depCfg.Imports = append(depCfg.Imports, &config.DependencyImport{ + Type: config.ImageRepoImport, + TargetBuildArg: dep.TargetBuildArgImageRepo, + }) + } + if dep.TargetBuildArgImageTag != "" { + depCfg.Imports = append(depCfg.Imports, &config.DependencyImport{ + Type: config.ImageTagImport, + TargetBuildArg: dep.TargetBuildArgImageTag, + }) + } + if dep.TargetBuildArgImageID != "" { + depCfg.Imports = append(depCfg.Imports, &config.DependencyImport{ + Type: config.ImageIDImport, + TargetBuildArg: dep.TargetBuildArgImageID, + }) + } + + return depCfg +} + +func GetConfigDependencies(dependencies []*TestDependency) (res []*config.Dependency) { + for _, dep := range dependencies { + res = append(res, dep.ToConfigDependency()) + } + + return +} + +func CheckImageDependenciesAfterPrepare(img *LegacyImageStub, dependencies []*TestDependency) { + for _, dep := range dependencies { + if dep.TargetEnvImageName != "" { + Expect(img._Container._ServiceCommitChangeOptions.Env[dep.TargetEnvImageName]).To(Equal(dep.GetDockerImageName())) + } + if dep.TargetEnvImageRepo != "" { + Expect(img._Container._ServiceCommitChangeOptions.Env[dep.TargetEnvImageRepo]).To(Equal(dep.DockerImageRepo)) + } + if dep.TargetEnvImageTag != "" { + Expect(img._Container._ServiceCommitChangeOptions.Env[dep.TargetEnvImageTag]).To(Equal(dep.DockerImageTag)) + } + if dep.TargetEnvImageID != "" { + Expect(img._Container._ServiceCommitChangeOptions.Env[dep.TargetEnvImageID]).To(Equal(dep.DockerImageID)) + } + + if dep.TargetBuildArgImageName != "" { + Expect(util.IsStringsContainValue(img._DockerfileImageBuilder.BuildDockerfileOptions.BuildArgs, fmt.Sprintf("%s=%s", dep.TargetBuildArgImageName, dep.GetDockerImageName()))) + } + if dep.TargetBuildArgImageRepo != "" { + Expect(util.IsStringsContainValue(img._DockerfileImageBuilder.BuildDockerfileOptions.BuildArgs, fmt.Sprintf("%s=%s", dep.TargetBuildArgImageRepo, dep.DockerImageRepo))) + } + if dep.TargetBuildArgImageTag != "" { + Expect(util.IsStringsContainValue(img._DockerfileImageBuilder.BuildDockerfileOptions.BuildArgs, fmt.Sprintf("%s=%s", dep.TargetBuildArgImageTag, dep.DockerImageTag))) + } + if dep.TargetBuildArgImageID != "" { + Expect(util.IsStringsContainValue(img._DockerfileImageBuilder.BuildDockerfileOptions.BuildArgs, fmt.Sprintf("%s=%s", dep.TargetBuildArgImageID, dep.DockerImageID))) + } + } +} diff --git a/pkg/build/stage/dependencies.go b/pkg/build/stage/dependencies.go index fc4364f5d5..487cd0a108 100644 --- a/pkg/build/stage/dependencies.go +++ b/pkg/build/stage/dependencies.go @@ -136,8 +136,6 @@ func (s *DependenciesStage) PrepareImage(ctx context.Context, c Conveyor, _, img depImageServiceOptions.AddEnv(map[string]string{ img.TargetEnv: depImageID, }) - default: - panic("unexpected configuration") } } } diff --git a/pkg/build/stage/dependencies_test.go b/pkg/build/stage/dependencies_test.go index a2f31d00ce..ee646b285b 100644 --- a/pkg/build/stage/dependencies_test.go +++ b/pkg/build/stage/dependencies_test.go @@ -2,367 +2,201 @@ package stage import ( "context" - "fmt" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - - "github.com/werf/werf/pkg/config" - "github.com/werf/werf/pkg/container_runtime" ) var _ = Describe("DependenciesStage", func() { - When("using image dependencies", func() { - type DepedenciesData struct { - Dependencies []*DependencyData - ExpectedDigest string - } - - DescribeTable("configuring images dependencies for dependencies stage", - func(data DepedenciesData) { - ctx := context.Background() - - conveyor := NewConveyorStubForDependencies(data.Dependencies) - - stage := newDependenciesStage(nil, GetConfigDependencies(data.Dependencies), "example-stage", &NewBaseStageOptions{ - ImageName: "example-image", - ProjectName: "example-project", - }) - - img := NewLegacyImageStub() - - digest, err := stage.GetDependencies(ctx, conveyor, nil, img) - Expect(err).To(Succeed()) - Expect(digest).To(Equal(data.ExpectedDigest)) - - err = stage.PrepareImage(ctx, conveyor, nil, img) - Expect(err).To(Succeed()) - CheckImageDependenciesAfterPrepare(img, data.Dependencies) - }, - - Entry("should calculate basic stage digest when no dependencies are set", - DepedenciesData{ - ExpectedDigest: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - }), - - Entry("should change stage digest and set configured environment variables when dependencies are set", - DepedenciesData{ - ExpectedDigest: "84f7d49084ba98f8247feba78a217382c6801c7df27cce294566cac69c43d58d", - Dependencies: []*DependencyData{ - { - ImageName: "one", - TargetEnvImageName: "IMAGE_ONE_NAME", - TargetEnvImageRepo: "IMAGE_ONE_REPO", - TargetEnvImageTag: "IMAGE_ONE_TAG", - - DockerImageRepo: "ONE_REPO", - DockerImageTag: "796e905d0cc975e718b3f8b3ea0199ea4d52668ecc12c4dbf85a136d-1638863657513", - DockerImageID: "sha256:d19deb06171086017db6aade408ce29592e7490f3b98d4da228ef6c771ddc6d5", - }, - { - ImageName: "two", - TargetEnvImageName: "TWO_NAME", - TargetEnvImageRepo: "TWO_REPO", - TargetEnvImageTag: "TWO_TAG", - TargetEnvImageID: "TWO_ID", - - DockerImageRepo: "TWO_REPO", - DockerImageTag: "bc6db8dde5c051349b85dbb8f858f4c80a519a17723d2c67dc9f890c-1643039584147", - DockerImageID: "sha256:5a46fe1fe7f2867aeb0a74cfc5aea79b1003b8d6095e2350332d3c99d7e1df6b", - }, - { - ImageName: "one", - TargetEnvImageName: "ONE_NAME", - TargetEnvImageRepo: "ONE_REPO", - TargetEnvImageTag: "ONE_TAG", - TargetEnvImageID: "ONE_ID", - - DockerImageRepo: "ONE_REPO", - DockerImageTag: "796e905d0cc975e718b3f8b3ea0199ea4d52668ecc12c4dbf85a136d-1638863657513", - DockerImageID: "sha256:d19deb06171086017db6aade408ce29592e7490f3b98d4da228ef6c771ddc6d5", - }, + DescribeTable("configuring images dependencies for dependencies stage", + func(data TestDependencies) { + ctx := context.Background() + + conveyor := NewConveyorStubForDependencies(NewGiterminismManagerStub(NewLocalGitRepoStub("9d8059842b6fde712c58315ca0ab4713d90761c0")), data.Dependencies) + + stage := newDependenciesStage(nil, GetConfigDependencies(data.Dependencies), "example-stage", &NewBaseStageOptions{ + ImageName: "example-image", + ProjectName: "example-project", + }) + + img := NewLegacyImageStub() + + digest, err := stage.GetDependencies(ctx, conveyor, nil, img) + Expect(err).To(Succeed()) + Expect(digest).To(Equal(data.ExpectedDigest)) + + err = stage.PrepareImage(ctx, conveyor, nil, img) + Expect(err).To(Succeed()) + CheckImageDependenciesAfterPrepare(img, data.Dependencies) + }, + + Entry("should calculate basic stage digest when no dependencies are set", + TestDependencies{ + ExpectedDigest: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + }), + + Entry("should change stage digest and set configured environment variables when dependencies are set", + TestDependencies{ + ExpectedDigest: "84f7d49084ba98f8247feba78a217382c6801c7df27cce294566cac69c43d58d", + Dependencies: []*TestDependency{ + { + ImageName: "one", + TargetEnvImageName: "IMAGE_ONE_NAME", + TargetEnvImageRepo: "IMAGE_ONE_REPO", + TargetEnvImageTag: "IMAGE_ONE_TAG", + + DockerImageRepo: "ONE_REPO", + DockerImageTag: "796e905d0cc975e718b3f8b3ea0199ea4d52668ecc12c4dbf85a136d-1638863657513", + DockerImageID: "sha256:d19deb06171086017db6aade408ce29592e7490f3b98d4da228ef6c771ddc6d5", }, - }), - - Entry("new image added into dependencies should change stage digest and environment variables", - DepedenciesData{ - ExpectedDigest: "d1af66208228e2be40cd861ac80d14d068f2c649d9fd345458efe3a48c2927b5", - Dependencies: []*DependencyData{ - { - ImageName: "one", - TargetEnvImageName: "IMAGE_ONE_NAME", - TargetEnvImageRepo: "IMAGE_ONE_REPO", - TargetEnvImageTag: "IMAGE_ONE_TAG", - - DockerImageRepo: "ONE_REPO", - DockerImageTag: "796e905d0cc975e718b3f8b3ea0199ea4d52668ecc12c4dbf85a136d-1638863657513", - DockerImageID: "sha256:d19deb06171086017db6aade408ce29592e7490f3b98d4da228ef6c771ddc6d5", - }, - { - ImageName: "two", - TargetEnvImageName: "TWO_NAME", - TargetEnvImageRepo: "TWO_REPO", - TargetEnvImageTag: "TWO_TAG", - TargetEnvImageID: "TWO_ID", - - DockerImageRepo: "TWO_REPO", - DockerImageTag: "bc6db8dde5c051349b85dbb8f858f4c80a519a17723d2c67dc9f890c-1643039584147", - DockerImageID: "sha256:5a46fe1fe7f2867aeb0a74cfc5aea79b1003b8d6095e2350332d3c99d7e1df6b", - }, - { - ImageName: "one", - TargetEnvImageName: "ONE_NAME", - TargetEnvImageRepo: "ONE_REPO", - TargetEnvImageTag: "ONE_TAG", - TargetEnvImageID: "ONE_ID", - - DockerImageRepo: "ONE_REPO", - DockerImageTag: "796e905d0cc975e718b3f8b3ea0199ea4d52668ecc12c4dbf85a136d-1638863657513", - DockerImageID: "sha256:d19deb06171086017db6aade408ce29592e7490f3b98d4da228ef6c771ddc6d5", - }, - { - ImageName: "three", - TargetEnvImageName: "THREE_IMAGE_NAME", - - DockerImageRepo: "THREE_REPO", - DockerImageTag: "custom-tag", - DockerImageID: "sha256:6f510109a5ca7657babd6f3f48fd16c1b887d63857ac411f636967de5aa48d31", - }, + { + ImageName: "two", + TargetEnvImageName: "TWO_NAME", + TargetEnvImageRepo: "TWO_REPO", + TargetEnvImageTag: "TWO_TAG", + TargetEnvImageID: "TWO_ID", + + DockerImageRepo: "TWO_REPO", + DockerImageTag: "bc6db8dde5c051349b85dbb8f858f4c80a519a17723d2c67dc9f890c-1643039584147", + DockerImageID: "sha256:5a46fe1fe7f2867aeb0a74cfc5aea79b1003b8d6095e2350332d3c99d7e1df6b", }, - }), - - Entry("should change stage digest and environment variables when previously added image dependency params has been changed", - DepedenciesData{ - ExpectedDigest: "d214e5d775ea7493e2fbe2f1d598d5c613a1c7fd605a55a4c4d98ab9d5161853", - Dependencies: []*DependencyData{ - { - ImageName: "one", - TargetEnvImageName: "IMAGE_ONE_NAME", - TargetEnvImageRepo: "IMAGE_ONE_REPO", - TargetEnvImageTag: "IMAGE_ONE_TAG", - - DockerImageRepo: "ONE_REPO", - DockerImageTag: "b7aebf280be3fbb7d207d3b659bfc1a49338441ea933c1eac5766a5f-1638863693022", - DockerImageID: "sha256:c62467775792f47c1bb39ceb5dccdafa02db1734f12c8aa07dbb6d618c501166", - }, - { - ImageName: "two", - TargetEnvImageName: "TWO_NAME", - TargetEnvImageRepo: "TWO_REPO", - TargetEnvImageTag: "TWO_TAG", - TargetEnvImageID: "TWO_ID", - - DockerImageRepo: "TWO_REPO", - DockerImageTag: "bc6db8dde5c051349b85dbb8f858f4c80a519a17723d2c67dc9f890c-1643039584147", - DockerImageID: "sha256:5a46fe1fe7f2867aeb0a74cfc5aea79b1003b8d6095e2350332d3c99d7e1df6b", - }, - { - ImageName: "one", - TargetEnvImageName: "ONE_NAME", - TargetEnvImageRepo: "ONE_REPO", - TargetEnvImageTag: "ONE_TAG", - TargetEnvImageID: "ONE_ID", - - DockerImageRepo: "ONE_REPO", - DockerImageTag: "b7aebf280be3fbb7d207d3b659bfc1a49338441ea933c1eac5766a5f-1638863693022", - DockerImageID: "sha256:c62467775792f47c1bb39ceb5dccdafa02db1734f12c8aa07dbb6d618c501166", - }, + { + ImageName: "one", + TargetEnvImageName: "ONE_NAME", + TargetEnvImageRepo: "ONE_REPO", + TargetEnvImageTag: "ONE_TAG", + TargetEnvImageID: "ONE_ID", + + DockerImageRepo: "ONE_REPO", + DockerImageTag: "796e905d0cc975e718b3f8b3ea0199ea4d52668ecc12c4dbf85a136d-1638863657513", + DockerImageID: "sha256:d19deb06171086017db6aade408ce29592e7490f3b98d4da228ef6c771ddc6d5", }, - }), - - Entry("should change stage digest and set configured environment variables when dependant image environment variable has been changed", - DepedenciesData{ - ExpectedDigest: "d0f6634579c776b6db5789d9c20e1f36a4c03bc7057a575d6965e4513fa27f8c", - Dependencies: []*DependencyData{ - { - ImageName: "one", - TargetEnvImageName: "IMAGE_ONE_NAME", - TargetEnvImageRepo: "IMAGE_ONE_REPO", - TargetEnvImageTag: "IMAGE_ONE_TAG_VARIABLE", - - DockerImageRepo: "ONE_REPO", - DockerImageTag: "796e905d0cc975e718b3f8b3ea0199ea4d52668ecc12c4dbf85a136d-1638863657513", - DockerImageID: "sha256:d19deb06171086017db6aade408ce29592e7490f3b98d4da228ef6c771ddc6d5", - }, - { - ImageName: "two", - TargetEnvImageName: "TWO_NAME", - TargetEnvImageRepo: "TWO_REPO", - TargetEnvImageTag: "TWO_TAG", - TargetEnvImageID: "TWO_ID", - - DockerImageRepo: "TWO_REPO", - DockerImageTag: "bc6db8dde5c051349b85dbb8f858f4c80a519a17723d2c67dc9f890c-1643039584147", - DockerImageID: "sha256:5a46fe1fe7f2867aeb0a74cfc5aea79b1003b8d6095e2350332d3c99d7e1df6b", - }, - { - ImageName: "one", - TargetEnvImageName: "ONE_NAME", - TargetEnvImageRepo: "ONE_REPO", - TargetEnvImageTag: "ONE_TAG", - TargetEnvImageID: "ONE_ID", + }, + }), + + Entry("new image added into dependencies should change stage digest and environment variables", + TestDependencies{ + ExpectedDigest: "d1af66208228e2be40cd861ac80d14d068f2c649d9fd345458efe3a48c2927b5", + Dependencies: []*TestDependency{ + { + ImageName: "one", + TargetEnvImageName: "IMAGE_ONE_NAME", + TargetEnvImageRepo: "IMAGE_ONE_REPO", + TargetEnvImageTag: "IMAGE_ONE_TAG", + + DockerImageRepo: "ONE_REPO", + DockerImageTag: "796e905d0cc975e718b3f8b3ea0199ea4d52668ecc12c4dbf85a136d-1638863657513", + DockerImageID: "sha256:d19deb06171086017db6aade408ce29592e7490f3b98d4da228ef6c771ddc6d5", + }, + { + ImageName: "two", + TargetEnvImageName: "TWO_NAME", + TargetEnvImageRepo: "TWO_REPO", + TargetEnvImageTag: "TWO_TAG", + TargetEnvImageID: "TWO_ID", + + DockerImageRepo: "TWO_REPO", + DockerImageTag: "bc6db8dde5c051349b85dbb8f858f4c80a519a17723d2c67dc9f890c-1643039584147", + DockerImageID: "sha256:5a46fe1fe7f2867aeb0a74cfc5aea79b1003b8d6095e2350332d3c99d7e1df6b", + }, + { + ImageName: "one", + TargetEnvImageName: "ONE_NAME", + TargetEnvImageRepo: "ONE_REPO", + TargetEnvImageTag: "ONE_TAG", + TargetEnvImageID: "ONE_ID", + + DockerImageRepo: "ONE_REPO", + DockerImageTag: "796e905d0cc975e718b3f8b3ea0199ea4d52668ecc12c4dbf85a136d-1638863657513", + DockerImageID: "sha256:d19deb06171086017db6aade408ce29592e7490f3b98d4da228ef6c771ddc6d5", + }, + { + ImageName: "three", + TargetEnvImageName: "THREE_IMAGE_NAME", - DockerImageRepo: "ONE_REPO", - DockerImageTag: "796e905d0cc975e718b3f8b3ea0199ea4d52668ecc12c4dbf85a136d-1638863657513", - DockerImageID: "sha256:d19deb06171086017db6aade408ce29592e7490f3b98d4da228ef6c771ddc6d5", - }, + DockerImageRepo: "THREE_REPO", + DockerImageTag: "custom-tag", + DockerImageID: "sha256:6f510109a5ca7657babd6f3f48fd16c1b887d63857ac411f636967de5aa48d31", + }, + }, + }), + + Entry("should change stage digest and environment variables when previously added image dependency params has been changed", + TestDependencies{ + ExpectedDigest: "d214e5d775ea7493e2fbe2f1d598d5c613a1c7fd605a55a4c4d98ab9d5161853", + Dependencies: []*TestDependency{ + { + ImageName: "one", + TargetEnvImageName: "IMAGE_ONE_NAME", + TargetEnvImageRepo: "IMAGE_ONE_REPO", + TargetEnvImageTag: "IMAGE_ONE_TAG", + + DockerImageRepo: "ONE_REPO", + DockerImageTag: "b7aebf280be3fbb7d207d3b659bfc1a49338441ea933c1eac5766a5f-1638863693022", + DockerImageID: "sha256:c62467775792f47c1bb39ceb5dccdafa02db1734f12c8aa07dbb6d618c501166", + }, + { + ImageName: "two", + TargetEnvImageName: "TWO_NAME", + TargetEnvImageRepo: "TWO_REPO", + TargetEnvImageTag: "TWO_TAG", + TargetEnvImageID: "TWO_ID", + + DockerImageRepo: "TWO_REPO", + DockerImageTag: "bc6db8dde5c051349b85dbb8f858f4c80a519a17723d2c67dc9f890c-1643039584147", + DockerImageID: "sha256:5a46fe1fe7f2867aeb0a74cfc5aea79b1003b8d6095e2350332d3c99d7e1df6b", + }, + { + ImageName: "one", + TargetEnvImageName: "ONE_NAME", + TargetEnvImageRepo: "ONE_REPO", + TargetEnvImageTag: "ONE_TAG", + TargetEnvImageID: "ONE_ID", + + DockerImageRepo: "ONE_REPO", + DockerImageTag: "b7aebf280be3fbb7d207d3b659bfc1a49338441ea933c1eac5766a5f-1638863693022", + DockerImageID: "sha256:c62467775792f47c1bb39ceb5dccdafa02db1734f12c8aa07dbb6d618c501166", + }, + }, + }), + + Entry("should change stage digest and set configured environment variables when dependant image environment variable has been changed", + TestDependencies{ + ExpectedDigest: "d0f6634579c776b6db5789d9c20e1f36a4c03bc7057a575d6965e4513fa27f8c", + Dependencies: []*TestDependency{ + { + ImageName: "one", + TargetEnvImageName: "IMAGE_ONE_NAME", + TargetEnvImageRepo: "IMAGE_ONE_REPO", + TargetEnvImageTag: "IMAGE_ONE_TAG_VARIABLE", + + DockerImageRepo: "ONE_REPO", + DockerImageTag: "796e905d0cc975e718b3f8b3ea0199ea4d52668ecc12c4dbf85a136d-1638863657513", + DockerImageID: "sha256:d19deb06171086017db6aade408ce29592e7490f3b98d4da228ef6c771ddc6d5", + }, + { + ImageName: "two", + TargetEnvImageName: "TWO_NAME", + TargetEnvImageRepo: "TWO_REPO", + TargetEnvImageTag: "TWO_TAG", + TargetEnvImageID: "TWO_ID", + + DockerImageRepo: "TWO_REPO", + DockerImageTag: "bc6db8dde5c051349b85dbb8f858f4c80a519a17723d2c67dc9f890c-1643039584147", + DockerImageID: "sha256:5a46fe1fe7f2867aeb0a74cfc5aea79b1003b8d6095e2350332d3c99d7e1df6b", }, - }), - ) - }) + { + ImageName: "one", + TargetEnvImageName: "ONE_NAME", + TargetEnvImageRepo: "ONE_REPO", + TargetEnvImageTag: "ONE_TAG", + TargetEnvImageID: "ONE_ID", + + DockerImageRepo: "ONE_REPO", + DockerImageTag: "796e905d0cc975e718b3f8b3ea0199ea4d52668ecc12c4dbf85a136d-1638863657513", + DockerImageID: "sha256:d19deb06171086017db6aade408ce29592e7490f3b98d4da228ef6c771ddc6d5", + }, + }, + }), + ) }) - -type LegacyImageStub struct { - container_runtime.LegacyImageInterface - - _Container *LegacyContainerStub -} - -func NewLegacyImageStub() *LegacyImageStub { - return &LegacyImageStub{ - _Container: NewLegacyContainerStub(), - } -} - -func (img *LegacyImageStub) Container() container_runtime.LegacyContainer { - return img._Container -} - -type LegacyContainerStub struct { - container_runtime.LegacyContainer - - _ServiceCommitChangeOptions *LegacyContainerOptionsStub -} - -func NewLegacyContainerStub() *LegacyContainerStub { - return &LegacyContainerStub{ - _ServiceCommitChangeOptions: NewLegacyContainerOptionsStub(), - } -} - -func (c *LegacyContainerStub) ServiceCommitChangeOptions() container_runtime.LegacyContainerOptions { - return c._ServiceCommitChangeOptions -} - -type LegacyContainerOptionsStub struct { - container_runtime.LegacyContainerOptions - - Env map[string]string -} - -func NewLegacyContainerOptionsStub() *LegacyContainerOptionsStub { - return &LegacyContainerOptionsStub{Env: make(map[string]string)} -} - -func (opts *LegacyContainerOptionsStub) AddEnv(envs map[string]string) { - for k, v := range envs { - opts.Env[k] = v - } -} - -type ConveyorStub struct { - Conveyor - - lastStageImageNameByImageName map[string]string - lastStageImageIDByImageName map[string]string -} - -func NewConveyorStub(lastStageImageNameByImageName, lastStageImageIDByImageName map[string]string) *ConveyorStub { - return &ConveyorStub{ - lastStageImageNameByImageName: lastStageImageNameByImageName, - lastStageImageIDByImageName: lastStageImageIDByImageName, - } -} - -func NewConveyorStubForDependencies(dependencies []*DependencyData) *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(lastStageImageNameByImageName, lastStageImageIDByImageName) -} - -func (c *ConveyorStub) GetImageNameForLastImageStage(imageName string) string { - return c.lastStageImageNameByImageName[imageName] -} - -func (c *ConveyorStub) GetImageIDForLastImageStage(imageName string) string { - return c.lastStageImageIDByImageName[imageName] -} - -type DependencyData struct { - ImageName string - - TargetEnvImageName string - TargetEnvImageRepo string - TargetEnvImageTag string - TargetEnvImageID string - - DockerImageRepo string - DockerImageTag string - DockerImageID string -} - -func (dep *DependencyData) GetDockerImageName() string { - return fmt.Sprintf("%s:%s", dep.DockerImageRepo, dep.DockerImageTag) -} - -func (dep *DependencyData) ToConfigDependency() *config.Dependency { - depCfg := &config.Dependency{ImageName: dep.ImageName} - - if dep.TargetEnvImageName != "" { - depCfg.Imports = append(depCfg.Imports, &config.DependencyImport{ - Type: config.ImageNameImport, - TargetEnv: dep.TargetEnvImageName, - }) - } - if dep.TargetEnvImageRepo != "" { - depCfg.Imports = append(depCfg.Imports, &config.DependencyImport{ - Type: config.ImageRepoImport, - TargetEnv: dep.TargetEnvImageRepo, - }) - } - if dep.TargetEnvImageTag != "" { - depCfg.Imports = append(depCfg.Imports, &config.DependencyImport{ - Type: config.ImageTagImport, - TargetEnv: dep.TargetEnvImageTag, - }) - } - if dep.TargetEnvImageID != "" { - depCfg.Imports = append(depCfg.Imports, &config.DependencyImport{ - Type: config.ImageIDImport, - TargetEnv: dep.TargetEnvImageID, - }) - } - - return depCfg -} - -func GetConfigDependencies(dependencies []*DependencyData) (res []*config.Dependency) { - for _, dep := range dependencies { - res = append(res, dep.ToConfigDependency()) - } - - return -} - -func CheckImageDependenciesAfterPrepare(img *LegacyImageStub, dependencies []*DependencyData) { - for _, dep := range dependencies { - if dep.TargetEnvImageName != "" { - Expect(img._Container._ServiceCommitChangeOptions.Env[dep.TargetEnvImageName]).To(Equal(dep.GetDockerImageName())) - } - if dep.TargetEnvImageRepo != "" { - Expect(img._Container._ServiceCommitChangeOptions.Env[dep.TargetEnvImageRepo]).To(Equal(dep.DockerImageRepo)) - } - if dep.TargetEnvImageTag != "" { - Expect(img._Container._ServiceCommitChangeOptions.Env[dep.TargetEnvImageTag]).To(Equal(dep.DockerImageTag)) - } - if dep.TargetEnvImageID != "" { - Expect(img._Container._ServiceCommitChangeOptions.Env[dep.TargetEnvImageID]).To(Equal(dep.DockerImageID)) - } - } -} diff --git a/pkg/build/stage/dockerfile.go b/pkg/build/stage/dockerfile.go index ee1608e776..81b98e3cad 100644 --- a/pkg/build/stage/dockerfile.go +++ b/pkg/build/stage/dockerfile.go @@ -16,6 +16,7 @@ import ( "github.com/moby/buildkit/frontend/dockerfile/shell" "github.com/werf/logboek" + "github.com/werf/werf/pkg/config" "github.com/werf/werf/pkg/container_runtime" "github.com/werf/werf/pkg/context_manager" "github.com/werf/werf/pkg/docker_registry" @@ -26,21 +27,24 @@ import ( "github.com/werf/werf/pkg/util" ) -func GenerateDockerfileStage(dockerRunArgs *DockerRunArgs, dockerStages *DockerStages, contextChecksum *ContextChecksum, baseStageOptions *NewBaseStageOptions) *DockerfileStage { - return newDockerfileStage(dockerRunArgs, dockerStages, contextChecksum, baseStageOptions) +func GenerateDockerfileStage(dockerRunArgs *DockerRunArgs, dockerStages *DockerStages, contextChecksum *ContextChecksum, baseStageOptions *NewBaseStageOptions, dependencies []*config.Dependency) *DockerfileStage { + return newDockerfileStage(dockerRunArgs, dockerStages, contextChecksum, baseStageOptions, dependencies) } -func newDockerfileStage(dockerRunArgs *DockerRunArgs, dockerStages *DockerStages, contextChecksum *ContextChecksum, baseStageOptions *NewBaseStageOptions) *DockerfileStage { +func newDockerfileStage(dockerRunArgs *DockerRunArgs, dockerStages *DockerStages, contextChecksum *ContextChecksum, baseStageOptions *NewBaseStageOptions, dependencies []*config.Dependency) *DockerfileStage { s := &DockerfileStage{} s.DockerRunArgs = dockerRunArgs s.DockerStages = dockerStages s.ContextChecksum = contextChecksum s.BaseStage = newBaseStage(Dockerfile, baseStageOptions) + s.dependencies = dependencies return s } type DockerfileStage struct { + dependencies []*config.Dependency + *DockerRunArgs *DockerStages *ContextChecksum @@ -90,25 +94,31 @@ type DockerStages struct { dockerStages []instructions.Stage dockerTargetStageIndex int dockerBuildArgsHash map[string]string - dockerMetaArgsHash map[string]string + dockerMetaArgs []instructions.ArgCommand dockerStageArgsHash map[int]map[string]string dockerStageEnvs map[int]map[string]string imageOnBuildInstructions map[string][]string } -func NewDockerStages(dockerStages []instructions.Stage, dockerBuildArgsHash map[string]string, dockerMetaArgs []instructions.ArgCommand, dockerTargetStageIndex int) (*DockerStages, error) { +func NewDockerStages(dockerStages []instructions.Stage, dockerBuildArgsHash map[string]string, dockerMetaArgs []instructions.ArgCommand, dockerTargetStageIndex int) *DockerStages { ds := &DockerStages{ dockerStages: dockerStages, dockerTargetStageIndex: dockerTargetStageIndex, dockerBuildArgsHash: dockerBuildArgsHash, + dockerMetaArgs: dockerMetaArgs, dockerStageArgsHash: map[int]map[string]string{}, dockerStageEnvs: map[int]map[string]string{}, imageOnBuildInstructions: map[string][]string{}, } - ds.dockerMetaArgsHash = map[string]string{} - for _, argInstruction := range dockerMetaArgs { + return ds +} + +func (ds *DockerStages) resolveDockerMetaArgs(resolvedDependenciesArgsHash map[string]string) (map[string]string, error) { + resolved := map[string]string{} + + for _, argInstruction := range ds.dockerMetaArgs { for _, keyValuePairOptional := range argInstruction.Args { key := keyValuePairOptional.Key @@ -117,61 +127,98 @@ func NewDockerStages(dockerStages []instructions.Stage, dockerBuildArgsHash map[ value = *keyValuePairOptional.Value } - if _, _, err := ds.addDockerMetaArg(key, value); err != nil { - return nil, err + resolvedKey, resolvedValue, err := ds.resolveDockerMetaArg(key, value, resolved, resolvedDependenciesArgsHash) + if err != nil { + return nil, fmt.Errorf("unable to resolve docker meta arg: %w", err) } + + resolved[resolvedKey] = resolvedValue } } - return ds, nil + return resolved, nil } // addDockerMetaArg function sets --build-arg value or resolved meta ARG value -func (ds *DockerStages) addDockerMetaArg(key, value string) (string, string, error) { - resolvedKey, err := ds.ShlexProcessWordWithMetaArgs(key) +func (ds *DockerStages) resolveDockerMetaArg(key, value string, resolvedDockerMetaArgsHash map[string]string, resolvedDependenciesArgsHash map[string]string) (string, string, error) { + resolvedKey, err := ds.ShlexProcessWordWithMetaArgs(key, resolvedDockerMetaArgsHash) if err != nil { return "", "", err } var resolvedValue string - if buildArgValue, ok := ds.dockerBuildArgsHash[resolvedKey]; ok { - resolvedValue = buildArgValue + + dependencyArgValue, ok := resolvedDependenciesArgsHash[resolvedKey] + if ok { + resolvedValue = dependencyArgValue } else { - rValue, err := ds.ShlexProcessWordWithMetaArgs(value) - if err != nil { - return "", "", err - } + if buildArgValue, ok := ds.dockerBuildArgsHash[resolvedKey]; ok { + resolvedValue = buildArgValue + } else { + rValue, err := ds.ShlexProcessWordWithMetaArgs(value, resolvedDockerMetaArgsHash) + if err != nil { + return "", "", err + } - resolvedValue = rValue + resolvedValue = rValue + } } - ds.dockerMetaArgsHash[resolvedKey] = resolvedValue return resolvedKey, resolvedValue, err } -// AddDockerStageArg function sets --build-arg value or resolved dockerfile stage ARG value or resolved meta ARG value (if stage ARG value is empty) -func (ds *DockerStages) AddDockerStageArg(dockerStageID int, key, value string) (string, string, error) { +func resolveDependenciesArgsHash(dependencies []*config.Dependency, c Conveyor) map[string]string { + resolved := make(map[string]string) + + for _, dep := range dependencies { + depImageName := c.GetImageNameForLastImageStage(dep.ImageName) + depImageID := c.GetImageIDForLastImageStage(dep.ImageName) + depImageRepo, depImageTag := image.ParseRepositoryAndTag(depImageName) + + for _, img := range dep.Imports { + switch img.Type { + case config.ImageRepoImport: + resolved[img.TargetBuildArg] = depImageRepo + case config.ImageTagImport: + resolved[img.TargetBuildArg] = depImageTag + case config.ImageNameImport: + resolved[img.TargetBuildArg] = depImageName + case config.ImageIDImport: + resolved[img.TargetBuildArg] = depImageID + } + } + } + + return resolved +} + +// resolveDockerStageArg function sets dependency arg value, or --build-arg value, or resolved dockerfile stage ARG value, or resolved meta ARG value (if stage ARG value is empty) +func (ds *DockerStages) resolveDockerStageArg(dockerStageID int, key, value string, resolvedDockerMetaArgsHash map[string]string, resolvedDependenciesArgsHash map[string]string) (string, string, error) { resolvedKey, err := ds.ShlexProcessWordWithStageArgsAndEnvs(dockerStageID, key) if err != nil { return "", "", err } var resolvedValue string - buildArgValue, ok := ds.dockerBuildArgsHash[resolvedKey] - switch { - case ok: - resolvedValue = buildArgValue - case value == "": - resolvedValue = ds.dockerMetaArgsHash[resolvedKey] - default: - rValue, err := ds.ShlexProcessWordWithStageArgsAndEnvs(dockerStageID, value) - if err != nil { - return "", "", err + dependencyArgValue, ok := resolvedDependenciesArgsHash[resolvedKey] + if ok { + resolvedValue = dependencyArgValue + } else { + buildArgValue, ok := ds.dockerBuildArgsHash[resolvedKey] + switch { + case ok: + resolvedValue = buildArgValue + case value == "": + resolvedValue = resolvedDockerMetaArgsHash[resolvedKey] + default: + rValue, err := ds.ShlexProcessWordWithStageArgsAndEnvs(dockerStageID, value) + if err != nil { + return "", "", err + } + resolvedValue = rValue } - resolvedValue = rValue } - ds.DockerStageArgsHash(dockerStageID)[resolvedKey] = resolvedValue return resolvedKey, resolvedValue, nil } @@ -190,8 +237,8 @@ func (ds *DockerStages) AddDockerStageEnv(dockerStageID int, key, value string) return resolvedKey, resolvedValue, nil } -func (ds *DockerStages) ShlexProcessWordWithMetaArgs(value string) (string, error) { - return shlexProcessWord(value, toArgsArray(ds.dockerMetaArgsHash)) +func (ds *DockerStages) ShlexProcessWordWithMetaArgs(value string, resolvedDockerMetaArgsHash map[string]string) (string, error) { + return shlexProcessWord(value, toArgsArray(resolvedDockerMetaArgsHash)) } func (ds *DockerStages) ShlexProcessWordWithStageArgsAndEnvs(dockerStageID int, value string) (string, error) { @@ -265,10 +312,17 @@ type dockerfileInstructionInterface interface { Name() string } -func (s *DockerfileStage) FetchDependencies(ctx context.Context, _ Conveyor, containerRuntime container_runtime.ContainerRuntime) error { +func (s *DockerfileStage) FetchDependencies(ctx context.Context, c Conveyor, containerRuntime container_runtime.ContainerRuntime) error { + resolvedDependenciesArgsHash := resolveDependenciesArgsHash(s.dependencies, c) + + resolvedDockerMetaArgsHash, err := s.resolveDockerMetaArgs(resolvedDependenciesArgsHash) + if err != nil { + return fmt.Errorf("unable to resolve docker meta args: %w", err) + } + outerLoop: for ind, stage := range s.dockerStages { - resolvedBaseName, err := s.ShlexProcessWordWithMetaArgs(stage.BaseName) + resolvedBaseName, err := s.ShlexProcessWordWithMetaArgs(stage.BaseName, resolvedDockerMetaArgsHash) if err != nil { return err } @@ -347,6 +401,13 @@ func isUnsupportedMediaTypeError(err error) bool { var errImageNotExistLocally = errors.New("IMAGE_NOT_EXIST_LOCALLY") func (s *DockerfileStage) GetDependencies(ctx context.Context, c Conveyor, _, _ container_runtime.LegacyImageInterface) (string, error) { + resolvedDependenciesArgsHash := resolveDependenciesArgsHash(s.dependencies, c) + + resolvedDockerMetaArgsHash, err := s.resolveDockerMetaArgs(resolvedDependenciesArgsHash) + if err != nil { + return "", fmt.Errorf("unable to resolve docker meta args: %w", err) + } + var stagesDependencies [][]string var stagesOnBuildDependencies [][]string @@ -356,7 +417,7 @@ func (s *DockerfileStage) GetDependencies(ctx context.Context, c Conveyor, _, _ dependencies = append(dependencies, s.addHost...) - resolvedBaseName, err := s.ShlexProcessWordWithMetaArgs(stage.BaseName) + resolvedBaseName, err := s.ShlexProcessWordWithMetaArgs(stage.BaseName, resolvedDockerMetaArgsHash) if err != nil { return "", err } @@ -366,7 +427,7 @@ func (s *DockerfileStage) GetDependencies(ctx context.Context, c Conveyor, _, _ onBuildInstructions, ok := s.imageOnBuildInstructions[resolvedBaseName] if ok { for _, instruction := range onBuildInstructions { - _, iOnBuildDependencies, err := s.dockerfileOnBuildInstructionDependencies(ctx, c.GiterminismManager(), ind, instruction, true) + _, iOnBuildDependencies, err := s.dockerfileOnBuildInstructionDependencies(ctx, c.GiterminismManager(), resolvedDockerMetaArgsHash, resolvedDependenciesArgsHash, ind, instruction, true) if err != nil { return "", err } @@ -376,7 +437,7 @@ func (s *DockerfileStage) GetDependencies(ctx context.Context, c Conveyor, _, _ } for _, cmd := range stage.Commands { - cmdDependencies, cmdOnBuildDependencies, err := s.dockerfileInstructionDependencies(ctx, c.GiterminismManager(), ind, cmd, false, false) + cmdDependencies, cmdOnBuildDependencies, err := s.dockerfileInstructionDependencies(ctx, c.GiterminismManager(), resolvedDockerMetaArgsHash, resolvedDependenciesArgsHash, ind, cmd, false, false) if err != nil { return "", err } @@ -421,7 +482,7 @@ func (s *DockerfileStage) GetDependencies(ctx context.Context, c Conveyor, _, _ return util.Sha256Hash(dockerfileStageDependencies...), nil } -func (s *DockerfileStage) dockerfileInstructionDependencies(ctx context.Context, giterminismManager giterminism_manager.Interface, dockerStageID int, cmd interface{}, isOnbuildInstruction bool, isBaseImageOnbuildInstruction bool) ([]string, []string, error) { +func (s *DockerfileStage) dockerfileInstructionDependencies(ctx context.Context, giterminismManager giterminism_manager.Interface, resolvedDockerMetaArgsHash map[string]string, resolvedDependenciesArgsHash map[string]string, dockerStageID int, cmd interface{}, isOnbuildInstruction bool, isBaseImageOnbuildInstruction bool) ([]string, []string, error) { var dependencies []string var onBuildDependencies []string @@ -463,10 +524,12 @@ func (s *DockerfileStage) dockerfileInstructionDependencies(ctx context.Context, var resolvedKey, resolvedValue string var err error if !isOnbuildInstruction { - resolvedKey, resolvedValue, err = s.AddDockerStageArg(dockerStageID, key, value) + resolvedKey, resolvedValue, err = s.resolveDockerStageArg(dockerStageID, key, value, resolvedDockerMetaArgsHash, resolvedDependenciesArgsHash) if err != nil { return "", "", err } + + s.DockerStageArgsHash(dockerStageID)[resolvedKey] = resolvedValue } else { resolvedKey, resolvedValue, err = resolveKeyAndValueFunc(key, value) if err != nil { @@ -563,7 +626,7 @@ func (s *DockerfileStage) dockerfileInstructionDependencies(ctx context.Context, dependencies = append(dependencies, checksum) } case *instructions.OnbuildCommand: - cDependencies, cOnBuildDependencies, err := s.dockerfileOnBuildInstructionDependencies(ctx, giterminismManager, dockerStageID, c.Expression, false) + cDependencies, cOnBuildDependencies, err := s.dockerfileOnBuildInstructionDependencies(ctx, giterminismManager, resolvedDockerMetaArgsHash, resolvedDependenciesArgsHash, dockerStageID, c.Expression, false) if err != nil { return nil, nil, err } @@ -584,7 +647,7 @@ func (s *DockerfileStage) dockerfileInstructionDependencies(ctx context.Context, return dependencies, onBuildDependencies, nil } -func (s *DockerfileStage) dockerfileOnBuildInstructionDependencies(ctx context.Context, giterminismManager giterminism_manager.Interface, dockerStageID int, expression string, isBaseImageOnbuildInstruction bool) ([]string, []string, error) { +func (s *DockerfileStage) dockerfileOnBuildInstructionDependencies(ctx context.Context, giterminismManager giterminism_manager.Interface, resolvedDockerMetaArgsHash map[string]string, resolvedDependenciesArgsHash map[string]string, dockerStageID int, expression string, isBaseImageOnbuildInstruction bool) ([]string, []string, error) { p, err := parser.Parse(bytes.NewReader([]byte(expression))) if err != nil { return nil, nil, err @@ -600,7 +663,7 @@ func (s *DockerfileStage) dockerfileOnBuildInstructionDependencies(ctx context.C return nil, nil, err } - onBuildDependencies, _, err := s.dockerfileInstructionDependencies(ctx, giterminismManager, dockerStageID, cmd, true, isBaseImageOnbuildInstruction) + onBuildDependencies, _, err := s.dockerfileInstructionDependencies(ctx, giterminismManager, resolvedDockerMetaArgsHash, resolvedDependenciesArgsHash, dockerStageID, cmd, true, isBaseImageOnbuildInstruction) if err != nil { return nil, nil, err } @@ -614,7 +677,7 @@ func (s *DockerfileStage) PrepareImage(ctx context.Context, c Conveyor, _, img c return err } - if err := s.SetupDockerImageBuilder(img.DockerfileImageBuilder()); err != nil { + if err := s.SetupDockerImageBuilder(img.DockerfileImageBuilder(), c); err != nil { return err } @@ -664,7 +727,7 @@ func (s *DockerfileStage) prepareContextArchive(ctx context.Context, giterminism return archivePath, nil } -func (s *DockerfileStage) SetupDockerImageBuilder(b *container_runtime.DockerfileImageBuilder) error { +func (s *DockerfileStage) SetupDockerImageBuilder(b *container_runtime.DockerfileImageBuilder, c Conveyor) error { b.SetDockerfile(s.dockerfile) b.SetDockerfileCtxRelPath(s.dockerfilePath) @@ -678,6 +741,13 @@ func (s *DockerfileStage) SetupDockerImageBuilder(b *container_runtime.Dockerfil } } + resolvedDependenciesArgsHash := resolveDependenciesArgsHash(s.dependencies, c) + if len(resolvedDependenciesArgsHash) > 0 { + for key, value := range resolvedDependenciesArgsHash { + b.AppendBuildArgs(fmt.Sprintf("%s=%v", key, value)) + } + } + if len(s.addHost) > 0 { b.AppendAddHost(s.addHost...) } diff --git a/pkg/build/stage/dockerfile_test.go b/pkg/build/stage/dockerfile_test.go new file mode 100644 index 0000000000..fdc24effc6 --- /dev/null +++ b/pkg/build/stage/dockerfile_test.go @@ -0,0 +1,289 @@ +package stage + +import ( + "bytes" + "context" + + "github.com/moby/buildkit/frontend/dockerfile/instructions" + "github.com/moby/buildkit/frontend/dockerfile/parser" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/werf/werf/pkg/build/dockerfile_helpers" + "github.com/werf/werf/pkg/util" +) + +var _ = Describe("DockerfileStage", func() { + DescribeTable("configuring images dependencies for dockerfile stage", + func(data TestDockerfileDependencies) { + ctx := context.Background() + + conveyor := NewConveyorStubForDependencies(NewGiterminismManagerStub(NewLocalGitRepoStub("9d8059842b6fde712c58315ca0ab4713d90761c0")), data.TestDependencies.Dependencies) + + p, err := parser.Parse(bytes.NewReader(data.Dockerfile)) + Expect(err).To(Succeed()) + + dockerStages, dockerMetaArgs, err := instructions.Parse(p.AST) + Expect(err).To(Succeed()) + + dockerfile_helpers.ResolveDockerStagesFromValue(dockerStages) + + dockerTargetIndex, err := dockerfile_helpers.GetDockerTargetStageIndex(dockerStages, data.Target) + Expect(err).To(Succeed()) + + ds := NewDockerStages( + dockerStages, + util.MapStringInterfaceToMapStringString(data.BuildArgs), + dockerMetaArgs, + dockerTargetIndex, + ) + + stage := newDockerfileStage( + NewDockerRunArgs( + data.Dockerfile, + "no-such-path", + data.Target, + "", + nil, + data.BuildArgs, + nil, + "", + "", + ), + ds, + NewContextChecksum(nil), + &NewBaseStageOptions{ + ImageName: "example-image", + ProjectName: "example-project", + }, + GetConfigDependencies(data.TestDependencies.Dependencies), + ) + + img := NewLegacyImageStub() + + digest, err := stage.GetDependencies(ctx, conveyor, nil, img) + Expect(err).To(Succeed()) + Expect(digest).To(Equal(data.TestDependencies.ExpectedDigest)) + + err = stage.PrepareImage(ctx, conveyor, nil, img) + Expect(err).To(Succeed()) + CheckDockerfileImageDependenciesAfterPrepare(img, data.TestDependencies.Dependencies) + }, + + Entry("should calculate dockerfile stage digest when no dependencies are set", + TestDockerfileDependencies{ + Dockerfile: []byte(` +FROM alpine:latest +RUN echo hello +`), + TestDependencies: &TestDependencies{ + ExpectedDigest: "b9d5527ee7a7047747bce5fb5fd1d7ab2b687f141a91151620098b60c2ad0eae", + }, + }), + + Entry("should not change dockerfile stage digest when dependencies are defined, but build args not used", + TestDockerfileDependencies{ + Dockerfile: []byte(` +FROM alpine:latest +RUN echo hello +`), + TestDependencies: &TestDependencies{ + ExpectedDigest: "b9d5527ee7a7047747bce5fb5fd1d7ab2b687f141a91151620098b60c2ad0eae", + Dependencies: []*TestDependency{ + { + ImageName: "one", + TargetBuildArgImageName: "IMAGE_ONE_NAME", + + DockerImageRepo: "ONE_REPO", + DockerImageTag: "796e905d0cc975e718b3f8b3ea0199ea4d52668ecc12c4dbf85a136d-1638863657513", + DockerImageID: "sha256:d19deb06171086017db6aade408ce29592e7490f3b98d4da228ef6c771ddc6d5", + }, + }, + }, + }, + ), + + Entry("should change dockerfile stage digest when dependant image build args used in the Dockerfile", + TestDockerfileDependencies{ + Dockerfile: []byte(` +FROM alpine:latest + +ARG IMAGE_ONE_NAME +ARG IMAGE_ONE_REPO +ARG IMAGE_ONE_TAG +ARG IMAGE_ONE_ID + +RUN echo hello +RUN echo {"name": "${IMAGE_ONE_NAME}", "repo": "${IMAGE_ONE_REPO}", "tag": "${IMAGE_ONE_TAG}", "id": "${IMAGE_ONE_ID}"} >> images.json +`), + TestDependencies: &TestDependencies{ + ExpectedDigest: "b55701cfd33c5931e001e4d8ab24628df571afc7b7f647ca4083dab75aafff4d", + Dependencies: []*TestDependency{ + { + ImageName: "one", + TargetBuildArgImageName: "IMAGE_ONE_NAME", + TargetBuildArgImageRepo: "IMAGE_ONE_REPO", + TargetBuildArgImageTag: "IMAGE_ONE_TAG", + TargetBuildArgImageID: "IMAGE_ONE_ID", + + DockerImageRepo: "ONE_REPO", + DockerImageTag: "796e905d0cc975e718b3f8b3ea0199ea4d52668ecc12c4dbf85a136d-1638863657513", + DockerImageID: "sha256:d19deb06171086017db6aade408ce29592e7490f3b98d4da228ef6c771ddc6d5", + }, + }, + }, + }, + ), + + Entry("should change dockerfile stage digest when dependant image name changed", + TestDockerfileDependencies{ + Dockerfile: []byte(` +FROM alpine:latest + +ARG IMAGE_ONE_NAME +ARG IMAGE_ONE_REPO +ARG IMAGE_ONE_TAG +ARG IMAGE_ONE_ID + +RUN echo hello +RUN echo {"name": "${IMAGE_ONE_NAME}", "repo": "${IMAGE_ONE_REPO}", "tag": "${IMAGE_ONE_TAG}", "id": "${IMAGE_ONE_ID}"} >> images.json +`), + TestDependencies: &TestDependencies{ + ExpectedDigest: "dac644aa25871d0d902581d9c2a901ef753267082adedc19a3bee23a18cfca17", + Dependencies: []*TestDependency{ + { + ImageName: "one", + TargetBuildArgImageName: "IMAGE_ONE_NAME", + TargetBuildArgImageRepo: "IMAGE_ONE_REPO", + TargetBuildArgImageTag: "IMAGE_ONE_TAG", + TargetBuildArgImageID: "IMAGE_ONE_ID", + + DockerImageRepo: "ONE_REPO", + DockerImageTag: "b7aebf280be3fbb7d207d3b659bfc1a49338441ea933c1eac5766a5f-1638863693022", + DockerImageID: "sha256:d19deb06171086017db6aade408ce29592e7490f3b98d4da228ef6c771ddc6d5", + }, + }, + }, + }, + ), + + Entry("should change dockerfile stage digest when dependant image id changed", + TestDockerfileDependencies{ + Dockerfile: []byte(` +FROM alpine:latest + +ARG IMAGE_ONE_NAME +ARG IMAGE_ONE_REPO +ARG IMAGE_ONE_TAG +ARG IMAGE_ONE_ID + +RUN echo hello +RUN echo {"name": "${IMAGE_ONE_NAME}", "repo": "${IMAGE_ONE_REPO}", "tag": "${IMAGE_ONE_TAG}", "id": "${IMAGE_ONE_ID}"} >> images.json +`), + TestDependencies: &TestDependencies{ + ExpectedDigest: "914969761b92ec6a4a6eee5ad33c32c2d2c27b0b15fe4abf4f26c30755378ed4", + Dependencies: []*TestDependency{ + { + ImageName: "one", + TargetBuildArgImageName: "IMAGE_ONE_NAME", + TargetBuildArgImageRepo: "IMAGE_ONE_REPO", + TargetBuildArgImageTag: "IMAGE_ONE_TAG", + TargetBuildArgImageID: "IMAGE_ONE_ID", + + DockerImageRepo: "ONE_REPO", + DockerImageTag: "b7aebf280be3fbb7d207d3b659bfc1a49338441ea933c1eac5766a5f-1638863693022", + DockerImageID: "sha256:44b14c266507626ec1e3f1eb22fcbd9b935595ead56800f77110fc4e1e95689c", + }, + }, + }, + }, + ), + + Entry("should calculate dockerfile stage digest when no dependencies are set", + TestDockerfileDependencies{ + Dockerfile: []byte(` +ARG BASE_IMAGE=alpine:latest + +FROM ${BASE_IMAGE} +RUN echo hello +`), + TestDependencies: &TestDependencies{ + ExpectedDigest: "b9d5527ee7a7047747bce5fb5fd1d7ab2b687f141a91151620098b60c2ad0eae", + }, + }), + + Entry("should allow usage of dependency image as a base image in the dockerfile", + TestDockerfileDependencies{ + Dockerfile: []byte(` +ARG BASE_IMAGE=alpine:latest + +FROM ${BASE_IMAGE} +RUN echo hello +`), + TestDependencies: &TestDependencies{ + ExpectedDigest: "573c4bd0f7480e27c266d55d3a020c7ec4acaebebf897d29cad78fded3b725c7", + Dependencies: []*TestDependency{ + { + ImageName: "two", + TargetBuildArgImageName: "BASE_IMAGE", + + DockerImageRepo: "ubuntu", + DockerImageTag: "latest", + DockerImageID: "sha256:d13c942271d66cb0954c3ba93e143cd253421fe0772b8bed32c4c0077a546d4d", + }, + }, + }, + }, + ), + + Entry("should change dockerfile stage digest when base dependency image has changed", + TestDockerfileDependencies{ + Dockerfile: []byte(` +ARG BASE_IMAGE=alpine:latest + +FROM ${BASE_IMAGE} +RUN echo hello +`), + TestDependencies: &TestDependencies{ + ExpectedDigest: "5b66aa2c1c9f0bf3a9089c52f04ddf4e47af055c4d1fe69d272cba24df372121", + Dependencies: []*TestDependency{ + { + ImageName: "two", + TargetBuildArgImageName: "BASE_IMAGE", + + DockerImageRepo: "centos", + DockerImageTag: "latest", + DockerImageID: "sha256:5d0da3dc976460b72c77d94c8a1ad043720b0416bfc16c52c45d4847e53fadb6", + }, + }, + }, + }, + ), + ) +}) + +type TestDockerfileDependencies struct { + Dockerfile []byte + Target string + BuildArgs map[string]interface{} + + TestDependencies *TestDependencies +} + +func CheckDockerfileImageDependenciesAfterPrepare(img *LegacyImageStub, dependencies []*TestDependency) { + for _, dep := range dependencies { + if dep.TargetEnvImageName != "" { + Expect(img._Container._ServiceCommitChangeOptions.Env[dep.TargetEnvImageName]).To(Equal(dep.GetDockerImageName())) + } + if dep.TargetEnvImageRepo != "" { + Expect(img._Container._ServiceCommitChangeOptions.Env[dep.TargetEnvImageRepo]).To(Equal(dep.DockerImageRepo)) + } + if dep.TargetEnvImageTag != "" { + Expect(img._Container._ServiceCommitChangeOptions.Env[dep.TargetEnvImageTag]).To(Equal(dep.DockerImageTag)) + } + if dep.TargetEnvImageID != "" { + Expect(img._Container._ServiceCommitChangeOptions.Env[dep.TargetEnvImageID]).To(Equal(dep.DockerImageID)) + } + + } +} diff --git a/pkg/build/stage/stage_suite_test.go b/pkg/build/stage/stage_suite_test.go index 242de36534..b35cffd13f 100644 --- a/pkg/build/stage/stage_suite_test.go +++ b/pkg/build/stage/stage_suite_test.go @@ -1,4 +1,4 @@ -package stage_test +package stage import ( "testing" diff --git a/pkg/build/stage/stubs_test.go b/pkg/build/stage/stubs_test.go new file mode 100644 index 0000000000..9506f28e03 --- /dev/null +++ b/pkg/build/stage/stubs_test.go @@ -0,0 +1,169 @@ +package stage + +import ( + "context" + + . "github.com/onsi/gomega" + + "github.com/werf/werf/pkg/container_runtime" + "github.com/werf/werf/pkg/git_repo" + "github.com/werf/werf/pkg/giterminism_manager" +) + +type LegacyImageStub struct { + container_runtime.LegacyImageInterface + + _Container *LegacyContainerStub + _DockerfileImageBuilder *container_runtime.DockerfileImageBuilder +} + +func NewLegacyImageStub() *LegacyImageStub { + return &LegacyImageStub{ + _Container: NewLegacyContainerStub(), + } +} + +func (img *LegacyImageStub) Container() container_runtime.LegacyContainer { + return img._Container +} + +func (img *LegacyImageStub) DockerfileImageBuilder() *container_runtime.DockerfileImageBuilder { + if img._DockerfileImageBuilder == nil { + img._DockerfileImageBuilder = container_runtime.NewDockerfileImageBuilder(nil) + } + return img._DockerfileImageBuilder +} + +type LegacyContainerStub struct { + container_runtime.LegacyContainer + + _ServiceCommitChangeOptions *LegacyContainerOptionsStub +} + +func NewLegacyContainerStub() *LegacyContainerStub { + return &LegacyContainerStub{ + _ServiceCommitChangeOptions: NewLegacyContainerOptionsStub(), + } +} + +func (c *LegacyContainerStub) ServiceCommitChangeOptions() container_runtime.LegacyContainerOptions { + return c._ServiceCommitChangeOptions +} + +type LegacyContainerOptionsStub struct { + container_runtime.LegacyContainerOptions + + Env map[string]string +} + +func NewLegacyContainerOptionsStub() *LegacyContainerOptionsStub { + return &LegacyContainerOptionsStub{Env: make(map[string]string)} +} + +func (opts *LegacyContainerOptionsStub) AddEnv(envs map[string]string) { + for k, v := range envs { + opts.Env[k] = v + } +} + +type ConveyorStub struct { + Conveyor + + giterminismManager *GiterminismManagerStub + lastStageImageNameByImageName map[string]string + lastStageImageIDByImageName map[string]string +} + +func NewConveyorStub(giterminismManager *GiterminismManagerStub, lastStageImageNameByImageName, lastStageImageIDByImageName map[string]string) *ConveyorStub { + return &ConveyorStub{ + giterminismManager: giterminismManager, + lastStageImageNameByImageName: lastStageImageNameByImageName, + lastStageImageIDByImageName: lastStageImageIDByImageName, + } +} + +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) GetImageNameForLastImageStage(imageName string) string { + return c.lastStageImageNameByImageName[imageName] +} + +func (c *ConveyorStub) GetImageIDForLastImageStage(imageName string) string { + return c.lastStageImageIDByImageName[imageName] +} + +func (c *ConveyorStub) GiterminismManager() giterminism_manager.Interface { + return c.giterminismManager +} + +type GiterminismManagerStub struct { + giterminism_manager.Interface + + localGitRepo git_repo.GitRepo +} + +func NewGiterminismManagerStub(localGitRepo git_repo.GitRepo) *GiterminismManagerStub { + return &GiterminismManagerStub{ + localGitRepo: localGitRepo, + } +} + +func (manager *GiterminismManagerStub) RelativeToGitProjectDir() string { + return "" +} + +func (manager *GiterminismManagerStub) LocalGitRepo() git_repo.GitRepo { + return manager.localGitRepo +} + +func (manager *GiterminismManagerStub) Dev() bool { + return false +} + +func (manager *GiterminismManagerStub) HeadCommit() string { + commit, err := manager.localGitRepo.HeadCommitHash(context.Background()) + Expect(err).To(Succeed()) + return commit +} + +type LocalGitRepoStub struct { + git_repo.GitRepo + + headCommitHash string +} + +func NewLocalGitRepoStub(headCommitHash string) *LocalGitRepoStub { + return &LocalGitRepoStub{ + headCommitHash: headCommitHash, + } +} + +func (repo *LocalGitRepoStub) HeadCommitHash(ctx context.Context) (string, error) { + return repo.headCommitHash, nil +} + +func (repo *LocalGitRepoStub) GetOrCreateArchive(ctx context.Context, opts git_repo.ArchiveOptions) (git_repo.Archive, error) { + return NewGitRepoArchiveStub(), nil +} + +type GitRepoArchiveStub struct { + git_repo.Archive +} + +func NewGitRepoArchiveStub() *GitRepoArchiveStub { + return &GitRepoArchiveStub{} +} + +func (archive *GitRepoArchiveStub) GetFilePath() string { + return "no-such-file" +} diff --git a/pkg/git_repo/git_repo.go b/pkg/git_repo/git_repo.go index 9f990a44ac..0e92b4e71a 100644 --- a/pkg/git_repo/git_repo.go +++ b/pkg/git_repo/git_repo.go @@ -40,6 +40,11 @@ type GitRepo interface { String() string GetName() string IsLocal() bool + GetWorkTreeDir() string + RemoteOriginUrl(_ context.Context) (string, error) + IsShallowClone(ctx context.Context) (bool, error) + FetchOrigin(ctx context.Context) error + SyncWithOrigin(ctx context.Context) error CreateDetachedMergeCommit(ctx context.Context, fromCommit, toCommit string) (string, error) GetCommitTreeEntry(ctx context.Context, commit string, path string) (*ls_tree.LsTreeEntry, error) @@ -65,6 +70,9 @@ type GitRepo interface { ResolveCommitFilePath(ctx context.Context, commit, path string) (string, error) TagCommit(ctx context.Context, tag string) (string, error) WalkCommitFiles(ctx context.Context, commit string, dir string, pathMatcher path_matcher.PathMatcher, fileFunc func(notResolvedPath string) error) error + + StatusPathList(ctx context.Context, pathMatcher path_matcher.PathMatcher) (list []string, err error) + ValidateStatusResult(ctx context.Context, pathMatcher path_matcher.PathMatcher) error } type gitRepo interface { diff --git a/pkg/git_repo/local.go b/pkg/git_repo/local.go index 8791025729..25762688d7 100644 --- a/pkg/git_repo/local.go +++ b/pkg/git_repo/local.go @@ -119,6 +119,10 @@ func (repo *Local) IsLocal() bool { return true } +func (repo *Local) GetWorkTreeDir() string { + return repo.WorkTreeDir +} + func (repo *Local) PlainOpen() (*git.Repository, error) { repository, err := git.PlainOpenWithOptions(repo.WorkTreeDir, &git.PlainOpenOptions{EnableDotGitCommonDir: true}) if err != nil { diff --git a/pkg/git_repo/remote.go b/pkg/git_repo/remote.go index 3ee0564cb5..7a8db6cb4e 100644 --- a/pkg/git_repo/remote.go +++ b/pkg/git_repo/remote.go @@ -16,6 +16,7 @@ import ( "github.com/werf/lockgate" "github.com/werf/logboek" "github.com/werf/werf/pkg/git_repo/repo_handle" + "github.com/werf/werf/pkg/path_matcher" "github.com/werf/werf/pkg/true_git" "github.com/werf/werf/pkg/util" "github.com/werf/werf/pkg/util/timestamps" @@ -40,6 +41,10 @@ func (repo *Remote) IsLocal() bool { return false } +func (repo *Remote) GetWorkTreeDir() string { + panic("not implemented") +} + func (repo *Remote) ValidateEndpoint() error { if ep, err := transport.NewEndpoint(repo.Url); err != nil { return fmt.Errorf("bad url %q: %s", repo.Url, err) @@ -57,6 +62,14 @@ func (repo *Remote) GetMergeCommitParents(_ context.Context, commit string) ([]s return repo.getMergeCommitParents(repo.GetClonePath(), commit) } +func (repo *Remote) StatusPathList(ctx context.Context, pathMatcher path_matcher.PathMatcher) ([]string, error) { + panic("not implemented") +} + +func (repo *Remote) ValidateStatusResult(ctx context.Context, pathMatcher path_matcher.PathMatcher) error { + panic("not implemented") +} + func (repo *Remote) getFilesystemRelativePathByEndpoint() string { host := repo.Endpoint.Host if repo.Endpoint.Port > 0 { @@ -69,7 +82,7 @@ func (repo *Remote) GetClonePath() string { return filepath.Join(GetGitRepoCacheDir(), repo.getRepoID()) } -func (repo *Remote) RemoteOriginUrl() (string, error) { +func (repo *Remote) RemoteOriginUrl(_ context.Context) (string, error) { return repo.remoteOriginUrl(repo.GetClonePath()) } @@ -77,6 +90,10 @@ func (repo *Remote) IsEmpty(ctx context.Context) (bool, error) { return repo.isEmpty(ctx, repo.GetClonePath()) } +func (repo *Remote) IsShallowClone(ctx context.Context) (bool, error) { + panic("not implemented") +} + func (repo *Remote) IsAncestor(ctx context.Context, ancestorCommit, descendantCommit string) (bool, error) { return true_git.IsAncestor(ctx, ancestorCommit, descendantCommit, repo.GetClonePath()) } @@ -90,7 +107,7 @@ func (repo *Remote) CloneAndFetch(ctx context.Context) error { return nil } - return repo.Fetch(ctx) + return repo.FetchOrigin(ctx) } func (repo *Remote) isCloneExists() (bool, error) { @@ -189,7 +206,11 @@ func (repo *Remote) Clone(ctx context.Context) (bool, error) { }) } -func (repo *Remote) Fetch(ctx context.Context) error { +func (repo *Remote) SyncWithOrigin(ctx context.Context) error { + panic("not implemented") +} + +func (repo *Remote) FetchOrigin(ctx context.Context) error { if repo.IsDryRun { return nil } diff --git a/pkg/giterminism_manager/file_reader/file_reader.go b/pkg/giterminism_manager/file_reader/file_reader.go index 9cfe090d70..2eec47233e 100644 --- a/pkg/giterminism_manager/file_reader/file_reader.go +++ b/pkg/giterminism_manager/file_reader/file_reader.go @@ -32,7 +32,7 @@ type giterminismConfig interface { type sharedOptions interface { ProjectDir() string RelativeToGitProjectDir() string - LocalGitRepo() *git_repo.Local + LocalGitRepo() git_repo.GitRepo HeadCommit() string LooseGiterminism() bool Dev() bool diff --git a/pkg/giterminism_manager/file_reader/git.go b/pkg/giterminism_manager/file_reader/git.go index 68e6c9d891..66d2606cfd 100644 --- a/pkg/giterminism_manager/file_reader/git.go +++ b/pkg/giterminism_manager/file_reader/git.go @@ -31,7 +31,7 @@ func (r FileReader) gitRelativePathToProjectDirRelativePath(relToGitPath string) } func (r FileReader) isSubpathOfWorkTreeDir(absPath string) bool { - return util.IsSubpathOfBasePath(r.sharedOptions.LocalGitRepo().WorkTreeDir, absPath) + return util.IsSubpathOfBasePath(r.sharedOptions.LocalGitRepo().GetWorkTreeDir(), absPath) } func (r FileReader) ValidateFileByStatusResult(ctx context.Context, relPath string) error { diff --git a/pkg/giterminism_manager/inspector/inspector.go b/pkg/giterminism_manager/inspector/inspector.go index c38e9f12ab..87e8e284c9 100644 --- a/pkg/giterminism_manager/inspector/inspector.go +++ b/pkg/giterminism_manager/inspector/inspector.go @@ -34,7 +34,7 @@ type fileReader interface { type sharedOptions interface { RelativeToGitProjectDir() string - LocalGitRepo() *git_repo.Local + LocalGitRepo() git_repo.GitRepo HeadCommit() string LooseGiterminism() bool Dev() bool diff --git a/pkg/giterminism_manager/interface.go b/pkg/giterminism_manager/interface.go index eaaa3a5e2a..00b620a343 100644 --- a/pkg/giterminism_manager/interface.go +++ b/pkg/giterminism_manager/interface.go @@ -14,7 +14,7 @@ type Interface interface { FileReader() FileReader Inspector() Inspector - LocalGitRepo() *git_repo.Local + LocalGitRepo() git_repo.GitRepo HeadCommit() string ProjectDir() string RelativeToGitProjectDir() string diff --git a/pkg/giterminism_manager/manager.go b/pkg/giterminism_manager/manager.go index b1e30479e0..31f31c1c7c 100644 --- a/pkg/giterminism_manager/manager.go +++ b/pkg/giterminism_manager/manager.go @@ -82,14 +82,14 @@ func (s *sharedOptions) ProjectDir() string { } func (s *sharedOptions) RelativeToGitProjectDir() string { - return util.GetRelativeToBaseFilepath(s.LocalGitRepo().WorkTreeDir, s.projectDir) + return util.GetRelativeToBaseFilepath(s.localGitRepo.WorkTreeDir, s.projectDir) } func (s *sharedOptions) HeadCommit() string { return s.headCommit } -func (s *sharedOptions) LocalGitRepo() *git_repo.Local { +func (s *sharedOptions) LocalGitRepo() git_repo.GitRepo { return s.localGitRepo }