diff --git a/util/argo/argo.go b/util/argo/argo.go index 80f4c83a9d8f0..3a99b0d9383a1 100644 --- a/util/argo/argo.go +++ b/util/argo/argo.go @@ -8,6 +8,8 @@ import ( "sort" "strings" "time" + "os" + goio "io" "github.com/argoproj/gitops-engine/pkg/cache" "github.com/argoproj/gitops-engine/pkg/utils/kube" @@ -1003,3 +1005,29 @@ func IsValidContainerName(name string) bool { validationErrors := apimachineryvalidation.NameIsDNSLabel(name, false) return len(validationErrors) == 0 } + +// CopyFile has os.filemode, because despite currently (PR12508) we only need to +// copy files from appsrc repo to repo, being restricted to non-exec files, +// later it could be used to replace copying the cmp server with /bin/cp, +// therefore removing the dependency on one of the cli tools (going distroless) +func CopyFile(srcFile string, dstFile string, perm os.FileMode) error { + log.WithFields(log.Fields{ + "srcFile": srcFile, + "dstFile": dstFile, + "perm": perm, + }).Debugf("CopyFile called") + src, err := os.OpenFile(srcFile, os.O_RDONLY, 0644) + if err != nil { + return err + } + defer src.Close() + + dst, err := os.OpenFile(dstFile, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm) + if err != nil { + return err + } + defer dst.Close() + + _, err = goio.Copy(dst, src) + return err +} diff --git a/util/argo/argo_test.go b/util/argo/argo_test.go index b356751426768..99791de85f3a7 100644 --- a/util/argo/argo_test.go +++ b/util/argo/argo_test.go @@ -6,6 +6,8 @@ import ( "fmt" "path/filepath" "testing" + "os" + "hash/fnv" "github.com/argoproj/gitops-engine/pkg/utils/kube" "github.com/argoproj/gitops-engine/pkg/utils/kube/kubetest" @@ -1328,3 +1330,48 @@ func TestValidatePermissionsMultipleSources(t *testing.T) { assert.Len(t, conditions, 0) }) } + +func TestCopyFile(t *testing.T) { + + // check the testdir first + cwd, err := os.Getwd() + if err != nil { + t.Fatalf("Unable to get cwd: %v", err) + } + + testdir := filepath.Join(cwd, "testdata") + _, err = os.Lstat(testdir) + if err != nil { + t.Fatalf("Unable to stat testdir: %v", err) + } + + srcFile := filepath.Join(testdir, "copyfile.src") + // calculate the src file's checksum + srcdata, err := os.ReadFile(srcFile) + if err != nil { + t.Fatalf("Unable to read testfile: %v", err) + } + srch := fnv.New32a() + _, _ = srch.Write(srcdata) + srchash := srch.Sum32() + + // copy it over + dstFile := fmt.Sprintf("%s-dstcheck", srcFile) + err = CopyFile(srcFile, dstFile, 0644) + defer os.Remove(dstFile) + if err != nil { + t.Fatalf("CopyFile failed: %v", err) + } + + // calculate the dst checksum + // calculate the src file's checksum + dstdata, err := os.ReadFile(dstFile) + if err != nil { + t.Fatalf("Unable to read testfile dest: %v", err) + } + dsth := fnv.New32a() + _, _ = dsth.Write(dstdata) + dsthash := dsth.Sum32() + + assert.Equal(t, srchash, dsthash) +} diff --git a/util/argo/testdata/copyfile.src b/util/argo/testdata/copyfile.src new file mode 100644 index 0000000000000..2553fb7956a1f Binary files /dev/null and b/util/argo/testdata/copyfile.src differ