Introduce Helmfile's own filesystem abstraction to correctly unit test some components (#307)

Use abstracted FS

Signed-off-by: Arkaitz Jimenez <arkaitzj@gmail.com>

Signed-off-by: Arkaitz Jimenez <arkaitzj@gmail.com>
This commit is contained in:
Arkaitz Jimenez 2022-08-24 05:58:43 +02:00 committed by GitHub
parent a626664f46
commit cc33e7b7d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 513 additions and 425 deletions

View File

@ -17,6 +17,7 @@ import (
"go.uber.org/zap"
"github.com/helmfile/helmfile/pkg/argparser"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/plugins"
"github.com/helmfile/helmfile/pkg/remote"
@ -39,16 +40,7 @@ type App struct {
FileOrDir string
readFile func(string) ([]byte, error)
deleteFile func(string) error
fileExists func(string) (bool, error)
glob func(string) ([]string, error)
abs func(string) (string, error)
fileExistsAt func(string) bool
directoryExistsAt func(string) bool
getwd func() (string, error)
chdir func(string) error
fs *filesystem.FileSystem
remote *remote.Remote
@ -81,20 +73,11 @@ func New(conf ConfigProvider) *App {
FileOrDir: conf.FileOrDir(),
ValuesFiles: conf.StateValuesFiles(),
Set: conf.StateValuesSet(),
fs: filesystem.DefaultFileSystem(),
})
}
func Init(app *App) *App {
app.readFile = os.ReadFile
app.deleteFile = os.Remove
app.glob = filepath.Glob
app.abs = filepath.Abs
app.getwd = os.Getwd
app.chdir = os.Chdir
app.fileExistsAt = fileExistsAt
app.fileExists = fileExists
app.directoryExistsAt = directoryExistsAt
var err error
app.valsRuntime, err = plugins.ValsInstance()
if err != nil {
@ -619,19 +602,19 @@ func (a *App) within(dir string, do func() error) error {
return do()
}
prev, err := a.getwd()
prev, err := a.fs.Getwd()
if err != nil {
return fmt.Errorf("failed getting current working direcotyr: %v", err)
}
absDir, err := a.abs(dir)
absDir, err := a.fs.Abs(dir)
if err != nil {
return err
}
a.Logger.Debugf("changing working directory to \"%s\"", absDir)
if err := a.chdir(absDir); err != nil {
if err := a.fs.Chdir(absDir); err != nil {
return fmt.Errorf("failed changing working directory to \"%s\": %v", absDir, err)
}
@ -639,7 +622,7 @@ func (a *App) within(dir string, do func() error) error {
a.Logger.Debugf("changing working directory back to \"%s\"", prev)
if chdirBackErr := a.chdir(prev); chdirBackErr != nil {
if chdirBackErr := a.fs.Chdir(prev); chdirBackErr != nil {
if appErr != nil {
a.Logger.Warnf("%v", appErr)
}
@ -658,7 +641,7 @@ func (a *App) visitStateFiles(fileOrDir string, opts LoadOpts, do func(string, s
for _, relPath := range desiredStateFiles {
var file string
var dir string
if a.directoryExistsAt(relPath) {
if a.fs.DirectoryExistsAt(relPath) {
file = relPath
dir = relPath
} else {
@ -668,7 +651,7 @@ func (a *App) visitStateFiles(fileOrDir string, opts LoadOpts, do func(string, s
a.Logger.Debugf("processing file \"%s\" in directory \"%s\"", file, dir)
absd, errAbsDir := a.abs(dir)
absd, errAbsDir := a.fs.Abs(dir)
if errAbsDir != nil {
return errAbsDir
}
@ -690,20 +673,15 @@ func (a *App) loadDesiredStateFromYaml(file string, opts ...LoadOpts) (*state.He
}
ld := &desiredStateLoader{
readFile: a.readFile,
deleteFile: a.deleteFile,
fileExists: a.fileExists,
directoryExistsAt: a.directoryExistsAt,
env: a.Env,
namespace: a.Namespace,
chart: a.Chart,
logger: a.Logger,
abs: a.abs,
remote: a.remote,
fs: a.fs,
env: a.Env,
namespace: a.Namespace,
chart: a.Chart,
logger: a.Logger,
remote: a.remote,
overrideKubeContext: a.OverrideKubeContext,
overrideHelmBinary: a.OverrideHelmBinary,
glob: a.glob,
getHelm: a.getHelm,
valsRuntime: a.valsRuntime,
}
@ -995,7 +973,7 @@ func (a *App) visitStatesWithSelectorsAndRemoteSupport(fileOrDir string, converg
opts.Environment.OverrideValues = envvals
}
a.remote = remote.NewRemote(a.Logger, "", a.readFile, a.directoryExistsAt, a.fileExistsAt)
a.remote = remote.NewRemote(a.Logger, "", a.fs)
f := converge
if opts.Filter {
@ -1095,18 +1073,18 @@ func (a *App) findDesiredStateFiles(specifiedPath string, opts LoadOpts) ([]stri
var helmfileDir string
if specifiedPath != "" {
switch {
case a.fileExistsAt(specifiedPath):
case a.fs.FileExistsAt(specifiedPath):
return []string{specifiedPath}, nil
case a.directoryExistsAt(specifiedPath):
case a.fs.DirectoryExistsAt(specifiedPath):
helmfileDir = specifiedPath
default:
return []string{}, fmt.Errorf("specified state file %s is not found", specifiedPath)
}
} else {
var defaultFile string
if a.fileExistsAt(DefaultHelmfile) {
if a.fs.FileExistsAt(DefaultHelmfile) {
defaultFile = DefaultHelmfile
} else if a.fileExistsAt(DeprecatedHelmfile) {
} else if a.fs.FileExistsAt(DeprecatedHelmfile) {
log.Printf(
"warn: %s is being loaded: %s is deprecated in favor of %s. See https://github.com/roboll/helmfile/issues/25 for more information",
DeprecatedHelmfile,
@ -1117,7 +1095,7 @@ func (a *App) findDesiredStateFiles(specifiedPath string, opts LoadOpts) ([]stri
}
switch {
case a.directoryExistsAt(DefaultHelmfileDirectory):
case a.fs.DirectoryExistsAt(DefaultHelmfileDirectory):
if defaultFile != "" {
return []string{}, fmt.Errorf("configuration conlict error: you can have either %s or %s, but not both", defaultFile, DefaultHelmfileDirectory)
}
@ -1130,7 +1108,7 @@ func (a *App) findDesiredStateFiles(specifiedPath string, opts LoadOpts) ([]stri
}
}
files, err := a.glob(filepath.Join(helmfileDir, "*.y*ml"))
files, err := a.fs.Glob(filepath.Join(helmfileDir, "*.y*ml"))
if err != nil {
return []string{}, err
}
@ -1926,28 +1904,6 @@ func (a *App) writeValues(r *Run, c WriteValuesConfigProvider) (bool, []error) {
return true, errs
}
func fileExistsAt(path string) bool {
fileInfo, err := os.Stat(path)
return err == nil && fileInfo.Mode().IsRegular()
}
func fileExists(path string) (bool, error) {
_, err := os.Stat(path)
if err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
return true, nil
}
func directoryExistsAt(path string) bool {
fileInfo, err := os.Stat(path)
return err == nil && fileInfo.Mode().IsDir()
}
// Error is a wrapper around an error that adds context to the error.
type Error struct {
msg string
@ -2064,10 +2020,10 @@ func (c context) wrapErrs(errs ...error) error {
func (a *App) ShowCacheDir(c CacheConfigProvider) error {
fmt.Printf("Cache directory: %s\n", remote.CacheDir())
if !directoryExistsAt(remote.CacheDir()) {
if !a.fs.DirectoryExistsAt(remote.CacheDir()) {
return nil
}
dirs, err := os.ReadDir(remote.CacheDir())
dirs, err := a.fs.ReadDir(remote.CacheDir())
if err != nil {
return err
}
@ -2079,7 +2035,7 @@ func (a *App) ShowCacheDir(c CacheConfigProvider) error {
}
func (a *App) CleanCacheDir(c CacheConfigProvider) error {
if !directoryExistsAt(remote.CacheDir()) {
if !a.fs.DirectoryExistsAt(remote.CacheDir()) {
return nil
}
fmt.Printf("Cleaning up cache directory: %s\n", remote.CacheDir())

View File

@ -4,7 +4,6 @@ import (
"bufio"
"bytes"
"io"
"path/filepath"
"sync"
"testing"
@ -12,6 +11,7 @@ import (
"github.com/variantdev/vals"
"github.com/helmfile/helmfile/pkg/exectest"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/testhelper"
)
@ -90,8 +90,7 @@ func TestApply_3(t *testing.T) {
app := appWithFs(&App{
OverrideHelmBinary: DefaultHelmBinary,
glob: filepath.Glob,
abs: filepath.Abs,
fs: filesystem.DefaultFileSystem(),
OverrideKubeContext: "",
Env: "default",
Logger: logger,

View File

@ -4,7 +4,6 @@ import (
"bufio"
"bytes"
"io"
"path/filepath"
"sync"
"testing"
@ -12,6 +11,7 @@ import (
"github.com/variantdev/vals"
"github.com/helmfile/helmfile/pkg/exectest"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/testhelper"
)
@ -90,8 +90,7 @@ func TestApply_2(t *testing.T) {
app := appWithFs(&App{
OverrideHelmBinary: DefaultHelmBinary,
glob: filepath.Glob,
abs: filepath.Abs,
fs: filesystem.DefaultFileSystem(),
OverrideKubeContext: "default",
Env: "default",
Logger: logger,

View File

@ -4,7 +4,6 @@ import (
"bufio"
"bytes"
"io"
"path/filepath"
"sync"
"testing"
@ -13,6 +12,7 @@ import (
"github.com/variantdev/vals"
"github.com/helmfile/helmfile/pkg/exectest"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/testhelper"
)
@ -129,8 +129,7 @@ releases:
app := appWithFs(&App{
OverrideHelmBinary: DefaultHelmBinary,
glob: filepath.Glob,
abs: filepath.Abs,
fs: filesystem.DefaultFileSystem(),
OverrideKubeContext: "default",
Env: "default",
Logger: logger,

View File

@ -15,6 +15,7 @@ import (
"github.com/variantdev/vals"
"github.com/helmfile/helmfile/pkg/exectest"
ffs "github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/testhelper"
)
@ -131,8 +132,7 @@ releases:
app := appWithFs(&App{
OverrideHelmBinary: DefaultHelmBinary,
glob: filepath.Glob,
abs: filepath.Abs,
fs: ffs.DefaultFileSystem(),
OverrideKubeContext: "default",
Env: "default",
Logger: logger,

View File

@ -4,7 +4,6 @@ import (
"bufio"
"bytes"
"io"
"path/filepath"
"sync"
"testing"
@ -12,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/variantdev/vals"
ffs "github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/testhelper"
"github.com/helmfile/helmfile/pkg/testutil"
@ -145,8 +145,7 @@ releases:
app := appWithFs(&App{
OverrideHelmBinary: DefaultHelmBinary,
glob: filepath.Glob,
abs: filepath.Abs,
fs: ffs.DefaultFileSystem(),
OverrideKubeContext: "default",
Env: tc.environment,
Logger: logger,

View File

@ -4,7 +4,6 @@ import (
"bufio"
"bytes"
"io"
"path/filepath"
"sync"
"testing"
@ -12,6 +11,7 @@ import (
"github.com/variantdev/vals"
"github.com/helmfile/helmfile/pkg/exectest"
ffs "github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/testhelper"
)
@ -88,8 +88,7 @@ func TestSync(t *testing.T) {
app := appWithFs(&App{
OverrideHelmBinary: DefaultHelmBinary,
glob: filepath.Glob,
abs: filepath.Abs,
fs: ffs.DefaultFileSystem(),
OverrideKubeContext: "default",
Env: "default",
Logger: logger,

View File

@ -15,6 +15,7 @@ import (
"github.com/variantdev/vals"
"github.com/helmfile/helmfile/pkg/exectest"
ffs "github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/testhelper"
)
@ -131,8 +132,7 @@ releases:
app := appWithFs(&App{
OverrideHelmBinary: DefaultHelmBinary,
glob: filepath.Glob,
abs: filepath.Abs,
fs: &ffs.FileSystem{Glob: filepath.Glob},
OverrideKubeContext: "default",
Env: "default",
Logger: logger,

View File

@ -21,6 +21,7 @@ import (
"github.com/helmfile/helmfile/pkg/envvar"
"github.com/helmfile/helmfile/pkg/exectest"
ffs "github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/remote"
"github.com/helmfile/helmfile/pkg/state"
@ -40,14 +41,7 @@ func injectFs(app *App, fs *testhelper.TestFs) *App {
app.Set = make(map[string]interface{})
}
app.readFile = fs.ReadFile
app.glob = fs.Glob
app.abs = fs.Abs
app.getwd = fs.Getwd
app.chdir = fs.Chdir
app.fileExistsAt = fs.FileExistsAt
app.fileExists = fs.FileExists
app.directoryExistsAt = fs.DirectoryExistsAt
app.fs = fs.ToFileSystem()
return app
}
@ -1529,12 +1523,11 @@ func TestLoadDesiredStateFromYaml_DuplicateReleaseName(t *testing.T) {
}
return yamlContent, nil
}
fs := ffs.FromFileSystem(ffs.FileSystem{ReadFile: readFile})
app := &App{
OverrideHelmBinary: DefaultHelmBinary,
OverrideKubeContext: "default",
readFile: readFile,
glob: filepath.Glob,
abs: filepath.Abs,
fs: fs,
Env: "default",
Logger: helmexec.NewLogger(os.Stderr, "debug"),
}
@ -1593,16 +1586,11 @@ helmDefaults:
app := &App{
OverrideHelmBinary: DefaultHelmBinary,
OverrideKubeContext: "default",
readFile: testFs.ReadFile,
glob: testFs.Glob,
abs: testFs.Abs,
directoryExistsAt: testFs.DirectoryExistsAt,
fileExistsAt: testFs.FileExistsAt,
fileExists: testFs.FileExists,
fs: testFs.ToFileSystem(),
Env: "default",
Logger: helmexec.NewLogger(os.Stderr, "debug"),
}
app.remote = remote.NewRemote(app.Logger, "", app.readFile, app.directoryExistsAt, app.fileExistsAt)
app.remote = remote.NewRemote(app.Logger, "", app.fs)
expectNoCallsToHelm(app)
@ -1683,14 +1671,11 @@ helmDefaults:
})
app := &App{
OverrideHelmBinary: DefaultHelmBinary,
readFile: testFs.ReadFile,
fileExists: testFs.FileExists,
glob: testFs.Glob,
abs: testFs.Abs,
fs: testFs.ToFileSystem(),
Env: "default",
Logger: helmexec.NewLogger(os.Stderr, "debug"),
}
app.remote = remote.NewRemote(app.Logger, testFs.Cwd, testFs.ReadFile, testFs.DirectoryExistsAt, testFs.FileExistsAt)
app.remote = remote.NewRemote(app.Logger, testFs.Cwd, app.fs)
expectNoCallsToHelm(app)
@ -1762,14 +1747,11 @@ foo: FOO
})
app := &App{
OverrideHelmBinary: DefaultHelmBinary,
readFile: testFs.ReadFile,
fileExists: testFs.FileExists,
glob: testFs.Glob,
abs: testFs.Abs,
fs: testFs.ToFileSystem(),
Env: "default",
Logger: helmexec.NewLogger(os.Stderr, "debug"),
}
app.remote = remote.NewRemote(app.Logger, testFs.Cwd, testFs.ReadFile, testFs.DirectoryExistsAt, testFs.FileExistsAt)
app.remote = remote.NewRemote(app.Logger, testFs.Cwd, app.fs)
expectNoCallsToHelm(app)
@ -1828,14 +1810,11 @@ foo: FOO
})
app := &App{
OverrideHelmBinary: DefaultHelmBinary,
readFile: testFs.ReadFile,
fileExists: testFs.FileExists,
glob: testFs.Glob,
abs: testFs.Abs,
fs: testFs.ToFileSystem(),
Env: "default",
Logger: helmexec.NewLogger(os.Stderr, "debug"),
}
app.remote = remote.NewRemote(app.Logger, testFs.Cwd, testFs.ReadFile, testFs.DirectoryExistsAt, testFs.FileExistsAt)
app.remote = remote.NewRemote(app.Logger, testFs.Cwd, app.fs)
expectNoCallsToHelm(app)
@ -1912,14 +1891,11 @@ helmDefaults:
})
app := &App{
OverrideHelmBinary: DefaultHelmBinary,
readFile: testFs.ReadFile,
fileExists: testFs.FileExists,
glob: testFs.Glob,
abs: testFs.Abs,
fs: testFs.ToFileSystem(),
Env: "test",
Logger: helmexec.NewLogger(os.Stderr, "debug"),
}
app.remote = remote.NewRemote(app.Logger, testFs.Cwd, testFs.ReadFile, testFs.DirectoryExistsAt, testFs.FileExistsAt)
app.remote = remote.NewRemote(app.Logger, testFs.Cwd, app.fs)
expectNoCallsToHelm(app)
@ -1988,13 +1964,11 @@ releases:
})
app := &App{
OverrideHelmBinary: DefaultHelmBinary,
readFile: testFs.ReadFile,
glob: testFs.Glob,
abs: testFs.Abs,
fs: testFs.ToFileSystem(),
Env: "default",
Logger: helmexec.NewLogger(os.Stderr, "debug"),
}
app.remote = remote.NewRemote(app.Logger, testFs.Cwd, testFs.ReadFile, testFs.DirectoryExistsAt, testFs.FileExistsAt)
app.remote = remote.NewRemote(app.Logger, testFs.Cwd, app.fs)
expectNoCallsToHelm(app)
@ -2046,14 +2020,12 @@ releases:
})
app := &App{
OverrideHelmBinary: DefaultHelmBinary,
readFile: testFs.ReadFile,
glob: testFs.Glob,
abs: testFs.Abs,
fs: testFs.ToFileSystem(),
Env: "default",
Logger: helmexec.NewLogger(os.Stderr, "debug"),
}
app.remote = remote.NewRemote(app.Logger, testFs.Cwd, testFs.ReadFile, testFs.DirectoryExistsAt, testFs.FileExistsAt)
app.remote = remote.NewRemote(app.Logger, testFs.Cwd, app.fs)
expectNoCallsToHelm(app)
st, err := app.loadDesiredStateFromYaml(statePath, LoadOpts{Reverse: true})
@ -2103,13 +2075,11 @@ releases:
})
app := &App{
OverrideHelmBinary: DefaultHelmBinary,
readFile: testFs.ReadFile,
glob: testFs.Glob,
abs: testFs.Abs,
fs: testFs.ToFileSystem(),
Env: "default",
Logger: helmexec.NewLogger(os.Stderr, "debug"),
}
app.remote = remote.NewRemote(app.Logger, testFs.Cwd, testFs.ReadFile, testFs.DirectoryExistsAt, testFs.FileExistsAt)
app.remote = remote.NewRemote(app.Logger, testFs.Cwd, app.fs)
opts := LoadOpts{
CalleePath: statePath,
@ -2217,13 +2187,11 @@ services:
})
app := &App{
OverrideHelmBinary: DefaultHelmBinary,
readFile: testFs.ReadFile,
glob: testFs.Glob,
abs: testFs.Abs,
fs: testFs.ToFileSystem(),
Env: "default",
Logger: helmexec.NewLogger(os.Stderr, "debug"),
}
app.remote = remote.NewRemote(app.Logger, testFs.Cwd, testFs.ReadFile, testFs.DirectoryExistsAt, testFs.FileExistsAt)
app.remote = remote.NewRemote(app.Logger, testFs.Cwd, app.fs)
expectNoCallsToHelm(app)
@ -2656,8 +2624,7 @@ releases:
app := appWithFs(&App{
OverrideHelmBinary: DefaultHelmBinary,
glob: filepath.Glob,
abs: filepath.Abs,
fs: ffs.DefaultFileSystem(),
OverrideKubeContext: "default",
Env: "default",
Logger: logger,
@ -2729,8 +2696,6 @@ releases:
app := appWithFs(&App{
OverrideHelmBinary: DefaultHelmBinary,
glob: filepath.Glob,
abs: filepath.Abs,
OverrideKubeContext: "default",
Env: "default",
Logger: logger,
@ -2741,6 +2706,8 @@ releases:
valsRuntime: valsRuntime,
}, files)
fmt.Printf("CRAFTED APP WITH %p\n", app.fs.DirectoryExistsAt)
if err := app.Template(configImpl{}); err != nil {
t.Fatalf("%v", err)
}
@ -4282,8 +4249,7 @@ changing working directory back to "/path/to"
app := appWithFs(&App{
OverrideHelmBinary: DefaultHelmBinary,
glob: filepath.Glob,
abs: filepath.Abs,
fs: ffs.DefaultFileSystem(),
OverrideKubeContext: "default",
Env: "default",
Logger: logger,
@ -4465,8 +4431,7 @@ changing working directory back to "/path/to"
app := appWithFs(&App{
OverrideHelmBinary: DefaultHelmBinary,
glob: filepath.Glob,
abs: filepath.Abs,
fs: ffs.DefaultFileSystem(),
OverrideKubeContext: "default",
Env: "default",
Logger: logger,
@ -4523,8 +4488,7 @@ releases:
app := appWithFs(&App{
OverrideHelmBinary: DefaultHelmBinary,
glob: filepath.Glob,
abs: filepath.Abs,
fs: ffs.DefaultFileSystem(),
OverrideKubeContext: "default",
Env: "default",
Logger: logger,
@ -4570,8 +4534,7 @@ releases:
app := appWithFs(&App{
OverrideHelmBinary: DefaultHelmBinary,
glob: filepath.Glob,
abs: filepath.Abs,
fs: ffs.DefaultFileSystem(),
OverrideKubeContext: "default",
Env: "default",
Logger: logger,
@ -4631,8 +4594,7 @@ releases:
app := appWithFs(&App{
OverrideHelmBinary: DefaultHelmBinary,
glob: filepath.Glob,
abs: filepath.Abs,
fs: ffs.DefaultFileSystem(),
OverrideKubeContext: "default",
Env: "default",
Logger: logger,
@ -4693,8 +4655,7 @@ releases:
app := appWithFs(&App{
OverrideHelmBinary: DefaultHelmBinary,
glob: filepath.Glob,
abs: filepath.Abs,
fs: ffs.DefaultFileSystem(),
OverrideKubeContext: "default",
Env: "default",
Logger: logger,

View File

@ -11,6 +11,7 @@ import (
"go.uber.org/zap"
"github.com/helmfile/helmfile/pkg/environment"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/remote"
"github.com/helmfile/helmfile/pkg/state"
@ -27,14 +28,9 @@ type desiredStateLoader struct {
env string
namespace string
chart string
fs *filesystem.FileSystem
readFile func(string) ([]byte, error)
deleteFile func(string) error
fileExists func(string) (bool, error)
abs func(string) (string, error)
glob func(string) ([]string, error)
directoryExistsAt func(string) bool
getHelm func(*state.HelmState) helmexec.Interface
getHelm func(*state.HelmState) helmexec.Interface
remote *remote.Remote
logger *zap.SugaredLogger
@ -50,8 +46,8 @@ func (ld *desiredStateLoader) Load(f string, opts LoadOpts) (*state.HelmState, e
if opts.CalleePath == "" {
return nil, fmt.Errorf("bug: opts.CalleePath was nil: f=%s, opts=%v", f, opts)
}
storage := state.NewStorage(opts.CalleePath, ld.logger, ld.glob)
envld := state.NewEnvironmentValuesLoader(storage, ld.readFile, ld.logger, ld.remote)
storage := state.NewStorage(opts.CalleePath, ld.logger, ld.fs)
envld := state.NewEnvironmentValuesLoader(storage, ld.fs, ld.logger, ld.remote)
handler := state.MissingFileHandlerError
vals, err := envld.LoadEnvironmentValues(&handler, args, &environment.EmptyEnvironment)
if err != nil {
@ -120,7 +116,7 @@ func (ld *desiredStateLoader) loadFileWithOverrides(inheritedEnv, overrodeEnv *e
f = filepath.Join(baseDir, file)
}
fileBytes, err := ld.readFile(f)
fileBytes, err := ld.fs.ReadFile(f)
if err != nil {
return nil, err
}
@ -166,8 +162,7 @@ func (ld *desiredStateLoader) loadFileWithOverrides(inheritedEnv, overrodeEnv *e
}
func (a *desiredStateLoader) underlying() *state.StateCreator {
c := state.NewCreator(a.logger, a.readFile, a.fileExists, a.abs, a.glob, a.directoryExistsAt, a.valsRuntime, a.getHelm, a.overrideHelmBinary, a.remote)
c.DeleteFile = a.deleteFile
c := state.NewCreator(a.logger, a.fs, a.valsRuntime, a.getHelm, a.overrideHelmBinary, a.remote)
c.LoadFile = a.loadFile
return c
}

View File

@ -4,13 +4,13 @@ import (
"bufio"
"bytes"
"io"
"path/filepath"
"sync"
"testing"
"github.com/variantdev/vals"
"github.com/helmfile/helmfile/pkg/exectest"
ffs "github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/testhelper"
)
@ -83,8 +83,7 @@ func TestDestroy_2(t *testing.T) {
app := appWithFs(&App{
OverrideHelmBinary: DefaultHelmBinary,
glob: filepath.Glob,
abs: filepath.Abs,
fs: ffs.DefaultFileSystem(),
OverrideKubeContext: "",
Env: "default",
Logger: logger,

View File

@ -4,7 +4,6 @@ import (
"bufio"
"bytes"
"io"
"path/filepath"
"sync"
"testing"
@ -12,6 +11,7 @@ import (
"go.uber.org/zap"
"github.com/helmfile/helmfile/pkg/exectest"
ffs "github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/testhelper"
)
@ -124,8 +124,7 @@ func TestDestroy(t *testing.T) {
app := appWithFs(&App{
OverrideHelmBinary: DefaultHelmBinary,
glob: filepath.Glob,
abs: filepath.Abs,
fs: ffs.DefaultFileSystem(),
OverrideKubeContext: "default",
Env: "default",
Logger: logger,

View File

@ -4,7 +4,6 @@ import (
"bufio"
"bytes"
"io"
"path/filepath"
"sync"
"testing"
@ -12,6 +11,7 @@ import (
"github.com/variantdev/vals"
"github.com/helmfile/helmfile/pkg/exectest"
ffs "github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/testhelper"
)
@ -1060,8 +1060,7 @@ changing working directory back to "/path/to"
app := appWithFs(&App{
OverrideHelmBinary: DefaultHelmBinary,
glob: filepath.Glob,
abs: filepath.Abs,
fs: ffs.DefaultFileSystem(),
OverrideKubeContext: "",
Env: "default",
Logger: logger,

View File

@ -4,7 +4,6 @@ import (
"bufio"
"bytes"
"io"
"path/filepath"
"sync"
"testing"
@ -13,6 +12,7 @@ import (
"go.uber.org/zap"
"github.com/helmfile/helmfile/pkg/exectest"
ffs "github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/testhelper"
)
@ -1352,8 +1352,7 @@ changing working directory back to "/path/to"
app := appWithFs(&App{
OverrideHelmBinary: DefaultHelmBinary,
glob: filepath.Glob,
abs: filepath.Abs,
fs: ffs.DefaultFileSystem(),
OverrideKubeContext: overrideKubeContext,
Env: "default",
Logger: logger,

View File

@ -124,7 +124,7 @@ func (r *desiredStateLoader) twoPassRenderTemplateToYaml(inherited, overrode *en
}
tmplData := state.NewEnvironmentTemplateData(*finalEnv, r.namespace, vals)
secondPassRenderer := tmpl.NewFileRenderer(r.readFile, baseDir, tmplData)
secondPassRenderer := tmpl.NewFileRenderer(r.fs, baseDir, tmplData)
yamlBuf, err := secondPassRenderer.RenderTemplateContentToBuffer(content)
if err != nil {
if r.logger != nil {

View File

@ -17,16 +17,13 @@ import (
func makeLoader(files map[string]string, env string) (*desiredStateLoader, *testhelper.TestFs) {
testfs := testhelper.NewTestFs(files)
logger := helmexec.NewLogger(os.Stdout, "debug")
r := remote.NewRemote(logger, testfs.Cwd, testfs.ReadFile, testfs.DirectoryExistsAt, testfs.FileExistsAt)
r := remote.NewRemote(logger, testfs.Cwd, testfs.ToFileSystem())
return &desiredStateLoader{
env: env,
namespace: "namespace",
logger: helmexec.NewLogger(os.Stdout, "debug"),
readFile: testfs.ReadFile,
fileExists: testfs.FileExists,
abs: testfs.Abs,
glob: testfs.Glob,
remote: r,
env: env,
namespace: "namespace",
logger: helmexec.NewLogger(os.Stdout, "debug"),
fs: testfs.ToFileSystem(),
remote: r,
}, testfs
}

View File

@ -7,6 +7,7 @@ import (
"go.uber.org/zap"
"github.com/helmfile/helmfile/pkg/environment"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/tmpl"
)
@ -35,9 +36,9 @@ type Bus struct {
Chart string
Env environment.Environment
Fs *filesystem.FileSystem
ReadFile func(string) ([]byte, error)
Logger *zap.SugaredLogger
Logger *zap.SugaredLogger
}
func (bus *Bus) Trigger(evt string, evtErr error, context map[string]interface{}) (bool, error) {
@ -100,7 +101,7 @@ func (bus *Bus) Trigger(evt string, evtErr error, context map[string]interface{}
for k, v := range context {
data[k] = v
}
render := tmpl.NewTextRenderer(bus.ReadFile, bus.BasePath, data)
render := tmpl.NewTextRenderer(bus.Fs, bus.BasePath, data)
bus.Logger.Debugf("hook[%s]: triggered by event \"%s\"\n", name, evt)

View File

@ -9,6 +9,7 @@ import (
"go.uber.org/zap/zaptest/observer"
"github.com/helmfile/helmfile/pkg/environment"
ffs "github.com/helmfile/helmfile/pkg/filesystem"
)
type runner struct {
@ -154,7 +155,7 @@ func TestTrigger(t *testing.T) {
Namespace: "myns",
Env: environment.Environment{Name: "prod"},
Logger: zeLogger,
ReadFile: readFile,
Fs: &ffs.FileSystem{ReadFile: readFile},
}
bus.Runner = &runner{}

101
pkg/filesystem/fs.go Normal file
View File

@ -0,0 +1,101 @@
package filesystem
import (
"io/fs"
"os"
"path/filepath"
)
type FileSystem struct {
ReadFile func(string) ([]byte, error)
ReadDir func(string) ([]fs.DirEntry, error)
DeleteFile func(string) error
FileExists func(string) (bool, error)
Glob func(string) ([]string, error)
FileExistsAt func(string) bool
DirectoryExistsAt func(string) bool
Stat func(string) (os.FileInfo, error)
Getwd func() (string, error)
Chdir func(string) error
Abs func(string) (string, error)
}
func DefaultFileSystem() *FileSystem {
dfs := FileSystem{
ReadFile: os.ReadFile,
ReadDir: os.ReadDir,
DeleteFile: os.Remove,
Stat: os.Stat,
Glob: filepath.Glob,
Getwd: os.Getwd,
Chdir: os.Chdir,
Abs: filepath.Abs,
}
dfs.FileExistsAt = dfs.fileExistsAtDefault
dfs.DirectoryExistsAt = dfs.directoryExistsDefault
dfs.FileExists = dfs.fileExistsDefault
return &dfs
}
func FromFileSystem(params FileSystem) *FileSystem {
dfs := DefaultFileSystem()
if params.ReadFile != nil {
dfs.ReadFile = params.ReadFile
}
if params.ReadDir != nil {
dfs.ReadDir = params.ReadDir
}
if params.DeleteFile != nil {
dfs.DeleteFile = params.DeleteFile
}
if params.FileExists != nil {
dfs.FileExists = params.FileExists
}
if params.Glob != nil {
dfs.Glob = params.Glob
}
if params.FileExistsAt != nil {
dfs.FileExistsAt = params.FileExistsAt
}
if params.DirectoryExistsAt != nil {
dfs.DirectoryExistsAt = params.DirectoryExistsAt
}
if params.Stat != nil {
dfs.Stat = params.Stat
}
if params.Getwd != nil {
dfs.Getwd = params.Getwd
}
if params.Chdir != nil {
dfs.Chdir = params.Chdir
}
if params.Abs != nil {
dfs.Abs = params.Abs
}
return dfs
}
func (filesystem *FileSystem) fileExistsAtDefault(path string) bool {
fileInfo, err := filesystem.Stat(path)
return err == nil && fileInfo.Mode().IsRegular()
}
func (filesystem *FileSystem) fileExistsDefault(path string) (bool, error) {
_, err := filesystem.Stat(path)
if err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
return true, nil
}
func (filesystem *FileSystem) directoryExistsDefault(path string) bool {
fileInfo, err := filesystem.Stat(path)
return err == nil && fileInfo.Mode().IsDir()
}

98
pkg/filesystem/fs_test.go Normal file
View File

@ -0,0 +1,98 @@
package filesystem
import (
"errors"
"io/fs"
"os"
"strings"
"testing"
"time"
)
type TestFileInfo struct {
mode fs.FileMode
}
func (tfi TestFileInfo) Name() string { return "" }
func (tfi TestFileInfo) Size() int64 { return 0 }
func (tfi TestFileInfo) Mode() fs.FileMode { return tfi.mode }
func (tfi TestFileInfo) ModTime() time.Time { return time.Time{} }
func (tfi TestFileInfo) IsDir() bool { return tfi.mode.IsDir() }
func (tfi TestFileInfo) Sys() any { return nil }
func NewTestFileSystem() FileSystem {
replaceffs := FileSystem{
Stat: func(s string) (os.FileInfo, error) {
if strings.HasPrefix(s, "existing_file") {
return TestFileInfo{mode: 0}, nil
}
if strings.HasPrefix(s, "existing_dir") {
return TestFileInfo{mode: fs.ModeDir}, nil
}
return nil, errors.New("Error")
},
}
return *FromFileSystem(replaceffs)
}
func TestFs_fileExistsDefault(t *testing.T) {
ffs := NewTestFileSystem()
var exists, _ = ffs.FileExists("existing_file.txt")
if !exists {
t.Errorf("Expected file %s, not found", "existing_file.txt")
}
exists, _ = ffs.FileExists("non_existing_file.txt")
if exists {
t.Errorf("Not expected file %s, found", "non_existing_file.txt")
}
}
func TestFs_fileExistsAtDefault(t *testing.T) {
ffs := NewTestFileSystem()
var exists = ffs.FileExistsAt("existing_file.txt")
if !exists {
t.Errorf("Expected file %s, not found", "existing_file.txt")
}
exists = ffs.FileExistsAt("non_existing_file.txt")
if exists {
t.Errorf("Not expected file %s, found", "non_existing_file.txt")
}
exists = ffs.FileExistsAt("existing_dir")
if exists {
t.Errorf("Not expected file %s, found", "existing_dir")
}
}
func TestFs_directoryExistsDefault(t *testing.T) {
ffs := NewTestFileSystem()
var exists = ffs.DirectoryExistsAt("existing_dir")
if !exists {
t.Errorf("Expected file %s, not found", "existing_dir")
}
exists = ffs.DirectoryExistsAt("not_existing_dir")
if exists {
t.Errorf("Not expected file %s, found", "existing_dir")
}
}
func TestFs_DefaultBuilder(t *testing.T) {
ffs := DefaultFileSystem()
if ffs.ReadFile == nil ||
ffs.ReadDir == nil ||
ffs.DeleteFile == nil ||
ffs.FileExists == nil ||
ffs.Glob == nil ||
ffs.FileExistsAt == nil ||
ffs.DirectoryExistsAt == nil ||
ffs.Stat == nil ||
ffs.Getwd == nil ||
ffs.Chdir == nil ||
ffs.Abs == nil {
t.Errorf("Missing functions in DefaultFileSystem")
}
}

View File

@ -17,6 +17,7 @@ import (
"gopkg.in/yaml.v2"
"github.com/helmfile/helmfile/pkg/envvar"
"github.com/helmfile/helmfile/pkg/filesystem"
)
var disableInsecureFeatures bool
@ -43,11 +44,9 @@ type Remote struct {
// Getter is the underlying implementation of getter used for fetching remote files
Getter Getter
// ReadFile is the implementation of the file reader that reads a local file from the specified path.
// Filesystem abstraction
// Inject any implementation of your choice, like an im-memory impl for testing, os.ReadFile for the real-world use.
ReadFile func(string) ([]byte, error)
DirExists func(string) bool
FileExists func(string) bool
fs *filesystem.FileSystem
}
func (r *Remote) Unmarshal(src string, dst interface{}) error {
@ -87,7 +86,7 @@ func (r *Remote) GetBytes(goGetterSrc string) ([]byte, error) {
return nil, err
}
bytes, err := r.ReadFile(f)
bytes, err := r.fs.ReadFile(f)
if err != nil {
return nil, fmt.Errorf("read file: %v", err)
}
@ -99,7 +98,7 @@ func (r *Remote) GetBytes(goGetterSrc string) ([]byte, error) {
// If the argument was an URL, it fetches the remote directory contained within the URL,
// and returns the path to the file in the fetched directory
func (r *Remote) Locate(urlOrPath string) (string, error) {
if r.FileExists(urlOrPath) || r.DirExists(urlOrPath) {
if r.fs.FileExistsAt(urlOrPath) || r.fs.DirectoryExistsAt(urlOrPath) {
return urlOrPath, nil
}
fetched, err := r.Fetch(urlOrPath)
@ -217,11 +216,11 @@ func (r *Remote) Fetch(goGetterSrc string, cacheDirOpt ...string) (string, error
r.Logger.Debugf("cached dir: %s", cacheDirPath)
{
if r.FileExists(cacheDirPath) {
if r.fs.FileExistsAt(cacheDirPath) {
return "", fmt.Errorf("%s is not directory. please remove it so that variant could use it for dependency caching", getterDst)
}
if r.DirExists(cacheDirPath) {
if r.fs.DirectoryExistsAt(cacheDirPath) {
cached = true
}
}
@ -285,17 +284,15 @@ func (g *GoGetter) Get(wd, src, dst string) error {
return nil
}
func NewRemote(logger *zap.SugaredLogger, homeDir string, readFile func(string) ([]byte, error), dirExists func(string) bool, fileExists func(string) bool) *Remote {
func NewRemote(logger *zap.SugaredLogger, homeDir string, fs *filesystem.FileSystem) *Remote {
if disableInsecureFeatures {
panic("Remote sources are disabled due to 'DISABLE_INSECURE_FEATURES'")
}
remote := &Remote{
Logger: logger,
Home: homeDir,
Getter: &GoGetter{Logger: logger},
ReadFile: readFile,
DirExists: dirExists,
FileExists: fileExists,
Logger: logger,
Home: homeDir,
Getter: &GoGetter{Logger: logger},
fs: fs,
}
if remote.Home == "" {

View File

@ -55,12 +55,10 @@ func TestRemote_HttpsGitHub(t *testing.T) {
get: get,
}
remote := &Remote{
Logger: helmexec.NewLogger(os.Stderr, "debug"),
Home: CacheDir(),
Getter: getter,
ReadFile: testfs.ReadFile,
FileExists: testfs.FileExistsAt,
DirExists: testfs.DirectoryExistsAt,
Logger: helmexec.NewLogger(os.Stderr, "debug"),
Home: CacheDir(),
Getter: getter,
fs: testfs.ToFileSystem(),
}
// FYI, go-getter in the `dir` mode accepts URL like the below. So helmfile expects URLs similar to it:
@ -133,12 +131,10 @@ func TestRemote_SShGitHub(t *testing.T) {
get: get,
}
remote := &Remote{
Logger: helmexec.NewLogger(os.Stderr, "debug"),
Home: CacheDir(),
Getter: getter,
ReadFile: testfs.ReadFile,
FileExists: testfs.FileExistsAt,
DirExists: testfs.DirectoryExistsAt,
Logger: helmexec.NewLogger(os.Stderr, "debug"),
Home: CacheDir(),
Getter: getter,
fs: testfs.ToFileSystem(),
}
url := "git::ssh://git@github.com/cloudposse/helmfiles.git@releases/kiam.yaml?ref=0.40.0"
@ -205,12 +201,10 @@ func TestRemote_SShGitHub_WithSshKey(t *testing.T) {
get: get,
}
remote := &Remote{
Logger: helmexec.NewLogger(os.Stderr, "debug"),
Home: CacheDir(),
Getter: getter,
ReadFile: testfs.ReadFile,
FileExists: testfs.FileExistsAt,
DirExists: testfs.DirectoryExistsAt,
Logger: helmexec.NewLogger(os.Stderr, "debug"),
Home: CacheDir(),
Getter: getter,
fs: testfs.ToFileSystem(),
}
url := "git::ssh://git@github.com/cloudposse/helmfiles.git@releases/kiam.yaml?ref=0.40.0&sshkey=ZWNkc2Etc2hhMi1uaXN0cDI1NiBBQUFBRTJWalpITmhMWE5vWVRJdGJtbHpkSEF5TlRZQUFBQUlibWx6ZEhBeU5UWUFBQUJCQkJTU3dOY2xoVzQ2Vm9VR3dMQ3JscVRHYUdOVWdRVUVEUEptc1ZzdUViL2RBNUcrQk9YMWxGaUVMYU9HQ2F6bS9KQkR2V3Y2Y0ZDQUtVRjVocVJOUjdJPSA="

View File

@ -146,8 +146,8 @@ func (st *HelmState) mergeLockedDependencies() (*HelmState, error) {
depMan := NewChartDependencyManager(filename, st.logger)
if st.readFile != nil {
depMan.readFile = st.readFile
if st.fs.ReadFile != nil {
depMan.readFile = st.fs.ReadFile
}
return resolveDependencies(st, depMan, unresolved)

View File

@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"io"
"os"
"github.com/imdario/mergo"
"github.com/variantdev/vals"
@ -13,6 +12,7 @@ import (
"gopkg.in/yaml.v2"
"github.com/helmfile/helmfile/pkg/environment"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/maputil"
"github.com/helmfile/helmfile/pkg/remote"
@ -43,12 +43,7 @@ func (e *UndefinedEnvError) Error() string {
type StateCreator struct {
logger *zap.SugaredLogger
readFile func(string) ([]byte, error)
fileExists func(string) (bool, error)
abs func(string) (string, error)
glob func(string) ([]string, error)
DeleteFile func(string) error
directoryExistsAt func(string) bool
fs *filesystem.FileSystem
valsRuntime vals.Evaluator
@ -63,17 +58,12 @@ type StateCreator struct {
remote *remote.Remote
}
func NewCreator(logger *zap.SugaredLogger, readFile func(string) ([]byte, error), fileExists func(string) (bool, error), abs func(string) (string, error), glob func(string) ([]string, error), directoryExistsAt func(string) bool, valsRuntime vals.Evaluator, getHelm func(*HelmState) helmexec.Interface, overrideHelmBinary string, remote *remote.Remote) *StateCreator {
func NewCreator(logger *zap.SugaredLogger, fs *filesystem.FileSystem, valsRuntime vals.Evaluator, getHelm func(*HelmState) helmexec.Interface, overrideHelmBinary string, remote *remote.Remote) *StateCreator {
return &StateCreator{
logger: logger,
readFile: readFile,
fileExists: fileExists,
abs: abs,
glob: glob,
directoryExistsAt: directoryExistsAt,
Strict: true,
fs: fs,
valsRuntime: valsRuntime,
getHelm: getHelm,
@ -87,6 +77,7 @@ func NewCreator(logger *zap.SugaredLogger, readFile func(string) ([]byte, error)
func (c *StateCreator) Parse(content []byte, baseDir, file string) (*HelmState, error) {
var state HelmState
state.fs = c.fs
state.FilePath = file
state.basePath = baseDir
@ -132,12 +123,6 @@ func (c *StateCreator) Parse(content []byte, baseDir, file string) (*HelmState,
}
state.logger = c.logger
state.readFile = c.readFile
state.removeFile = os.Remove
state.fileExists = c.fileExists
state.glob = c.glob
state.directoryExistsAt = c.directoryExistsAt
state.valsRuntime = c.valsRuntime
return &state, nil
@ -147,7 +132,7 @@ func (c *StateCreator) Parse(content []byte, baseDir, file string) (*HelmState,
func (c *StateCreator) LoadEnvValues(target *HelmState, env string, ctxEnv *environment.Environment, failOnMissingEnv bool) (*HelmState, error) {
state := *target
e, err := c.loadEnvValues(&state, env, failOnMissingEnv, ctxEnv, c.readFile, c.glob)
e, err := c.loadEnvValues(&state, env, failOnMissingEnv, ctxEnv)
if err != nil {
return nil, &StateLoadError{fmt.Sprintf("failed to read %s", state.FilePath), err}
}
@ -222,7 +207,7 @@ func (c *StateCreator) loadBases(envValues *environment.Environment, st *HelmSta
}
// nolint: unparam
func (c *StateCreator) loadEnvValues(st *HelmState, name string, failOnMissingEnv bool, ctxEnv *environment.Environment, readFile func(string) ([]byte, error), glob func(string) ([]string, error)) (*environment.Environment, error) {
func (c *StateCreator) loadEnvValues(st *HelmState, name string, failOnMissingEnv bool, ctxEnv *environment.Environment) (*environment.Environment, error) {
envVals := map[string]interface{}{}
envSpec, ok := st.Environments[name]
if ok {
@ -245,7 +230,7 @@ func (c *StateCreator) loadEnvValues(st *HelmState, name string, failOnMissingEn
envSecretFiles = append(envSecretFiles, resolved...)
}
if err = c.scatterGatherEnvSecretFiles(st, envSecretFiles, envVals, readFile); err != nil {
if err = c.scatterGatherEnvSecretFiles(st, envSecretFiles, envVals); err != nil {
return nil, err
}
}
@ -268,7 +253,7 @@ func (c *StateCreator) loadEnvValues(st *HelmState, name string, failOnMissingEn
return newEnv, nil
}
func (c *StateCreator) scatterGatherEnvSecretFiles(st *HelmState, envSecretFiles []string, envVals map[string]interface{}, readFile func(string) ([]byte, error)) error {
func (c *StateCreator) scatterGatherEnvSecretFiles(st *HelmState, envSecretFiles []string, envVals map[string]interface{}) error {
var errs []error
helm := c.getHelm(st)
@ -308,11 +293,11 @@ func (c *StateCreator) scatterGatherEnvSecretFiles(st *HelmState, envSecretFiles
}
// nolint: staticcheck
defer func() {
if err := c.DeleteFile(decFile); err != nil {
if err := c.fs.DeleteFile(decFile); err != nil {
c.logger.Warnf("removing decrypted file %s: %w", decFile, err)
}
}()
bytes, err := readFile(decFile)
bytes, err := c.fs.ReadFile(decFile)
if err != nil {
results <- secretResult{secret.id, nil, fmt.Errorf("failed to load environment secrets file \"%s\": %v", secret.path, err), secret.path}
continue
@ -367,7 +352,7 @@ func (st *HelmState) loadValuesEntries(missingFileHandler *string, entries []int
var envVals map[string]interface{}
valuesEntries := append([]interface{}{}, entries...)
ld := NewEnvironmentValuesLoader(st.storage(), st.readFile, st.logger, remote)
ld := NewEnvironmentValuesLoader(st.storage(), st.fs, st.logger, remote)
var err error
envVals, err = ld.LoadEnvironmentValues(missingFileHandler, valuesEntries, ctxEnv)
if err != nil {

View File

@ -1,7 +1,6 @@
package state
import (
"os"
"path/filepath"
"reflect"
"testing"
@ -10,18 +9,16 @@ import (
"go.uber.org/zap"
"github.com/helmfile/helmfile/pkg/environment"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/remote"
"github.com/helmfile/helmfile/pkg/testhelper"
)
func createFromYaml(content []byte, file string, env string, logger *zap.SugaredLogger) (*HelmState, error) {
c := &StateCreator{
logger: logger,
readFile: os.ReadFile,
abs: filepath.Abs,
DeleteFile: os.Remove,
Strict: true,
logger: logger,
fs: filesystem.DefaultFileSystem(),
Strict: true,
}
return c.ParseAndLoad(content, filepath.Dir(file), file, env, true, nil)
}
@ -81,8 +78,8 @@ func (testEnv stateTestEnv) MustLoadState(t *testing.T, file, envName string) *H
t.Fatalf("no file named %q registered", file)
}
r := remote.NewRemote(logger, testFs.Cwd, testFs.ReadFile, testFs.DirectoryExistsAt, testFs.FileExistsAt)
state, err := NewCreator(logger, testFs.ReadFile, testFs.FileExists, testFs.Abs, testFs.Glob, testFs.DirectoryExistsAt, nil, nil, "", r).
r := remote.NewRemote(logger, testFs.Cwd, testFs.ToFileSystem())
state, err := NewCreator(logger, testFs.ToFileSystem(), nil, nil, "", r).
ParseAndLoad([]byte(yamlContent), filepath.Dir(file), file, envName, true, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
@ -148,11 +145,11 @@ releaseNamespace: mynamespace
})
testFs.Cwd = "/example/path/to"
r := remote.NewRemote(logger, testFs.Cwd, testFs.ReadFile, testFs.DirectoryExistsAt, testFs.FileExistsAt)
r := remote.NewRemote(logger, testFs.Cwd, testFs.ToFileSystem())
env := environment.Environment{
Name: "production",
}
state, err := NewCreator(logger, testFs.ReadFile, testFs.FileExists, testFs.Abs, testFs.Glob, testFs.DirectoryExistsAt, nil, nil, "", r).
state, err := NewCreator(logger, testFs.ToFileSystem(), nil, nil, "", r).
ParseAndLoad(yamlContent, filepath.Dir(yamlFile), yamlFile, "production", true, &env)
if err != nil {
t.Fatalf("unexpected error: %v", err)
@ -238,8 +235,8 @@ overrideNamespace: myns
})
testFs.Cwd = "/example/path/to"
r := remote.NewRemote(logger, testFs.Cwd, testFs.ReadFile, testFs.DirectoryExistsAt, testFs.FileExistsAt)
state, err := NewCreator(logger, testFs.ReadFile, testFs.FileExists, testFs.Abs, testFs.Glob, testFs.DirectoryExistsAt, nil, nil, "", r).
r := remote.NewRemote(logger, testFs.Cwd, testFs.ToFileSystem())
state, err := NewCreator(logger, testFs.ToFileSystem(), nil, nil, "", r).
ParseAndLoad(yamlContent, filepath.Dir(yamlFile), yamlFile, "production", true, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)

View File

@ -9,6 +9,7 @@ import (
"gopkg.in/yaml.v2"
"github.com/helmfile/helmfile/pkg/environment"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/maputil"
"github.com/helmfile/helmfile/pkg/remote"
"github.com/helmfile/helmfile/pkg/tmpl"
@ -17,19 +18,19 @@ import (
type EnvironmentValuesLoader struct {
storage *Storage
readFile func(string) ([]byte, error)
fs *filesystem.FileSystem
logger *zap.SugaredLogger
remote *remote.Remote
}
func NewEnvironmentValuesLoader(storage *Storage, readFile func(string) ([]byte, error), logger *zap.SugaredLogger, remote *remote.Remote) *EnvironmentValuesLoader {
func NewEnvironmentValuesLoader(storage *Storage, fs *filesystem.FileSystem, logger *zap.SugaredLogger, remote *remote.Remote) *EnvironmentValuesLoader {
return &EnvironmentValuesLoader{
storage: storage,
readFile: readFile,
logger: logger,
remote: remote,
storage: storage,
fs: fs,
logger: logger,
remote: remote,
}
}
@ -64,7 +65,7 @@ func (ld *EnvironmentValuesLoader) LoadEnvironmentValues(missingFileHandler *str
}
tmplData := NewEnvironmentTemplateData(env, "", map[string]interface{}{})
r := tmpl.NewFileRenderer(ld.readFile, filepath.Dir(f), tmplData)
r := tmpl.NewFileRenderer(ld.fs, filepath.Dir(f), tmplData)
bytes, err := r.RenderToBytes(f)
if err != nil {
return nil, fmt.Errorf("failed to load environment values file \"%s\": %v", f, err)

View File

@ -1,13 +1,12 @@
package state
import (
"os"
"path/filepath"
"testing"
"github.com/google/go-cmp/cmp"
"go.uber.org/zap"
ffs "github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/remote"
)
@ -22,14 +21,11 @@ func newLoader() *EnvironmentValuesLoader {
storage := &Storage{
FilePath: "./helmfile.yaml",
basePath: ".",
glob: filepath.Glob,
fs: ffs.DefaultFileSystem(),
logger: sugar,
}
readFile := func(s string) ([]byte, error) { return []byte{}, nil }
dirExists := func(d string) bool { return false }
fileExists := func(f string) bool { return false }
return NewEnvironmentValuesLoader(storage, os.ReadFile, sugar, remote.NewRemote(sugar, "/tmp", readFile, dirExists, fileExists))
return NewEnvironmentValuesLoader(storage, storage.fs, sugar, remote.NewRemote(sugar, "/tmp", storage.fs))
}
// See https://github.com/roboll/helmfile/pull/1169

View File

@ -26,16 +26,6 @@ func (st *HelmState) appendHelmXFlags(flags []string, release *ReleaseSpec) ([]s
return flags, nil
}
func fileExistsAt(path string) bool {
fileInfo, err := os.Stat(path)
return err == nil && fileInfo.Mode().IsRegular()
}
func directoryExistsAt(path string) bool {
fileInfo, err := os.Stat(path)
return err == nil && fileInfo.Mode().IsDir()
}
type Chartify struct {
Opts *chartify.ChartifyOpts
Clean func()
@ -70,7 +60,7 @@ func (st *HelmState) goGetterChart(chart, dir, cacheDir string, force bool) (str
return "", fmt.Errorf("Parsing url from dir failed due to error %q.\nContinuing the process assuming this is a regular Helm chart or a local dir.", err.Error())
}
} else {
r := remote.NewRemote(st.logger, "", st.readFile, directoryExistsAt, fileExistsAt)
r := remote.NewRemote(st.logger, "", st.fs)
fetchedDir, err := r.Fetch(chart, cacheDir)
if err != nil {
@ -107,14 +97,14 @@ func (st *HelmState) PrepareChartify(helm helmexec.Interface, release *ReleaseSp
dir = filepath.Join(st.basePath, chart)
}
if stat, _ := os.Stat(dir); stat != nil && stat.IsDir() {
if exists, err := st.fileExists(filepath.Join(dir, "Chart.yaml")); err == nil && !exists {
if exists, err := st.fs.FileExists(filepath.Join(dir, "Chart.yaml")); err == nil && !exists {
shouldRun = true
}
}
for _, d := range release.Dependencies {
chart := d.Chart
if st.directoryExistsAt(chart) {
if st.fs.DirectoryExistsAt(chart) {
var err error
// Otherwise helm-dependency-up on the temporary chart generated by chartify ends up errors like:

View File

@ -26,6 +26,7 @@ import (
"github.com/helmfile/helmfile/pkg/environment"
"github.com/helmfile/helmfile/pkg/event"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/remote"
"github.com/helmfile/helmfile/pkg/tmpl"
@ -92,14 +93,9 @@ type HelmState struct {
ReleaseSetSpec `yaml:",inline"`
logger *zap.SugaredLogger
readFile func(string) ([]byte, error)
removeFile func(string) error
fileExists func(string) (bool, error)
glob func(string) ([]string, error)
tempDir func(string, string) (string, error)
directoryExistsAt func(string) bool
logger *zap.SugaredLogger
fs *filesystem.FileSystem
tempDir func(string, string) (string, error)
valsRuntime vals.Evaluator
@ -553,7 +549,7 @@ func (st *HelmState) prepareSyncReleases(helm helmexec.Interface, additionalValu
errs = append(errs, newReleaseFailedError(release, err))
}
ok, err := st.fileExists(valfile)
ok, err := st.fs.FileExists(valfile)
if err != nil {
errs = append(errs, newReleaseFailedError(release, err))
} else if !ok {
@ -1118,7 +1114,7 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre
}
}
isLocal := st.directoryExistsAt(normalizeChart(st.basePath, chartName))
isLocal := st.fs.DirectoryExistsAt(normalizeChart(st.basePath, chartName))
chartification, clean, err := st.PrepareChartify(helm, release, chartPath, workerIndex)
if !opts.SkipCleanup {
@ -1172,7 +1168,7 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre
// Skip `helm dep build` and `helm dep up` altogether when the chart is from remote or the dep is
// explicitly skipped.
buildDeps = !skipDeps
} else if normalizedChart := normalizeChart(st.basePath, chartPath); st.directoryExistsAt(normalizedChart) {
} else if normalizedChart := normalizeChart(st.basePath, chartPath); st.fs.DirectoryExistsAt(normalizedChart) {
// At this point, we are sure that chartPath is a local directory containing either:
// - A remote chart fetched by go-getter or
// - A local chart
@ -1505,7 +1501,7 @@ func (st *HelmState) WriteReleasesValues(helm helmexec.Interface, additionalValu
for _, f := range append(generatedFiles, additionalValues...) {
src := map[string]interface{}{}
srcBytes, err := st.readFile(f)
srcBytes, err := st.fs.ReadFile(f)
if err != nil {
return []error{fmt.Errorf("reading %s: %w", f, err)}
}
@ -2236,7 +2232,7 @@ func (st *HelmState) triggerGlobalReleaseEvent(evt string, evtErr error, helmfil
Chart: st.OverrideChart,
Env: st.Env,
Logger: st.logger,
ReadFile: st.readFile,
Fs: st.fs,
}
data := map[string]interface{}{
"HelmfileCommand": helmfileCmd,
@ -2269,7 +2265,7 @@ func (st *HelmState) triggerReleaseEvent(evt string, evtErr error, r *ReleaseSpe
Chart: st.OverrideChart,
Env: st.Env,
Logger: st.logger,
ReadFile: st.readFile,
Fs: st.fs,
}
vals := st.Values()
data := map[string]interface{}{
@ -2307,7 +2303,7 @@ func (st *HelmState) UpdateDeps(helm helmexec.Interface, includeTransitiveNeeds
var errs []error
for _, release := range releases {
if st.directoryExistsAt(release.ChartPathOrName()) {
if st.fs.DirectoryExistsAt(release.ChartPathOrName()) {
if err := helm.UpdateDeps(release.ChartPathOrName()); err != nil {
errs = append(errs, err)
}
@ -2631,7 +2627,7 @@ func (st *HelmState) newReleaseTemplateData(release *ReleaseSpec) releaseTemplat
}
func (st *HelmState) newReleaseTemplateFuncMap(dir string) template.FuncMap {
r := tmpl.NewFileRenderer(st.readFile, dir, nil)
r := tmpl.NewFileRenderer(st.fs, dir, nil)
return r.Context.CreateFuncMap()
}
@ -2639,7 +2635,7 @@ func (st *HelmState) newReleaseTemplateFuncMap(dir string) template.FuncMap {
func (st *HelmState) RenderReleaseValuesFileToBytes(release *ReleaseSpec, path string) ([]byte, error) {
templateData := st.newReleaseTemplateData(release)
r := tmpl.NewFileRenderer(st.readFile, filepath.Dir(path), templateData)
r := tmpl.NewFileRenderer(st.fs, filepath.Dir(path), templateData)
rawBytes, err := r.RenderToBytes(path)
if err != nil {
return nil, err
@ -2673,8 +2669,8 @@ func (st *HelmState) storage() *Storage {
return &Storage{
FilePath: st.FilePath,
basePath: st.basePath,
glob: st.glob,
logger: st.logger,
fs: st.fs,
}
}
@ -2710,7 +2706,7 @@ func (st *HelmState) ExpandedHelmfiles() ([]SubHelmfileSpec, error) {
func (st *HelmState) removeFiles(files []string) {
for _, f := range files {
if err := st.removeFile(f); err != nil {
if err := st.fs.DeleteFile(f); err != nil {
st.logger.Warnf("Removing %s: %v", err)
} else {
st.logger.Debugf("Removed %s", f)

View File

@ -110,7 +110,7 @@ func (st *HelmState) ExecuteTemplates() (*HelmState, error) {
successFlag := false
for it, prev := 0, &release; it < 6; it++ {
tmplData := st.createReleaseTemplateData(prev, vals)
renderer := tmpl.NewFileRenderer(st.readFile, st.basePath, tmplData)
renderer := tmpl.NewFileRenderer(st.fs, st.basePath, tmplData)
r, err := release.ExecuteTemplateExpressions(renderer)
if err != nil {
return nil, fmt.Errorf("failed executing templates in release \"%s\".\"%s\": %v", st.FilePath, release.Name, err)

View File

@ -7,6 +7,7 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
)
@ -39,7 +40,7 @@ func TestGoGetter(t *testing.T) {
st := &HelmState{
logger: logger,
readFile: os.ReadFile,
fs: filesystem.DefaultFileSystem(),
basePath: d,
}

View File

@ -11,6 +11,7 @@ import (
"github.com/variantdev/vals"
"github.com/helmfile/helmfile/pkg/exectest"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/testhelper"
)
@ -19,10 +20,7 @@ var logger = helmexec.NewLogger(os.Stdout, "warn")
var valsRuntime, _ = vals.New(vals.Options{CacheSize: 32})
func injectFs(st *HelmState, fs *testhelper.TestFs) *HelmState {
st.glob = fs.Glob
st.readFile = fs.ReadFile
st.fileExists = fs.FileExists
st.directoryExistsAt = fs.DirectoryExistsAt
st.fs = fs.ToFileSystem()
return st
}
@ -1715,17 +1713,17 @@ func TestHelmState_SyncReleasesCleanup(t *testing.T) {
ReleaseSetSpec: ReleaseSetSpec{
Releases: tt.releases,
},
logger: logger,
valsRuntime: valsRuntime,
removeFile: func(f string) error {
numRemovedFiles += 1
return nil
},
logger: logger,
valsRuntime: valsRuntime,
RenderedValues: map[string]interface{}{},
}
testfs := testhelper.NewTestFs(map[string]string{
"/path/to/someFile": `foo: FOO`,
})
testfs.DeleteFile = func(f string) error {
numRemovedFiles += 1
return nil
}
state = injectFs(state, testfs)
if errs := state.SyncReleases(&AffectedReleases{}, tt.helm, []string{}, 1); len(errs) > 0 {
t.Errorf("unexpected errors: %v", errs)
@ -1802,18 +1800,18 @@ func TestHelmState_DiffReleasesCleanup(t *testing.T) {
ReleaseSetSpec: ReleaseSetSpec{
Releases: tt.releases,
},
logger: logger,
valsRuntime: valsRuntime,
removeFile: func(f string) error {
numRemovedFiles += 1
return nil
},
logger: logger,
valsRuntime: valsRuntime,
RenderedValues: map[string]interface{}{},
}
testfs := testhelper.NewTestFs(map[string]string{
"/path/to/someFile": `foo: bar
`,
})
testfs.DeleteFile = func(f string) error {
numRemovedFiles += 1
return nil
}
state = injectFs(state, testfs)
if _, errs := state.DiffReleases(tt.helm, []string{}, 1, false, false, []string{}, false, false, false, false); len(errs) > 0 {
t.Errorf("unexpected errors: %v", errs)
@ -1960,11 +1958,13 @@ func TestHelmState_ResolveDeps_NoLockFile(t *testing.T) {
},
},
logger: logger,
readFile: func(f string) ([]byte, error) {
if f != "helmfile.lock" {
return nil, fmt.Errorf("stub: unexpected file: %s", f)
}
return nil, os.ErrNotExist
fs: &filesystem.FileSystem{
ReadFile: func(f string) ([]byte, error) {
if f != "helmfile.lock" {
return nil, fmt.Errorf("stub: unexpected file: %s", f)
}
return nil, os.ErrNotExist
},
},
}
@ -2053,17 +2053,19 @@ func TestHelmState_ReleaseStatuses(t *testing.T) {
Releases: tt.releases,
},
logger: logger,
fileExists: func(f string) (bool, error) {
if f != "foo.yaml" {
return false, fmt.Errorf("unexpected file: %s", f)
}
return true, nil
},
readFile: func(f string) ([]byte, error) {
if f != "foo.yaml" {
return nil, fmt.Errorf("unexpected file: %s", f)
}
return []byte{}, nil
fs: &filesystem.FileSystem{
FileExists: func(f string) (bool, error) {
if f != "foo.yaml" {
return false, fmt.Errorf("unexpected file: %s", f)
}
return true, nil
},
ReadFile: func(f string) ([]byte, error) {
if f != "foo.yaml" {
return nil, fmt.Errorf("unexpected file: %s", f)
}
return []byte{}, nil
},
},
}
errs := state.ReleaseStatuses(tt.helm, 1)

View File

@ -8,6 +8,7 @@ import (
"go.uber.org/zap"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/remote"
)
@ -16,17 +17,16 @@ type Storage struct {
FilePath string
readFile func(string) ([]byte, error)
basePath string
glob func(string) ([]string, error)
fs *filesystem.FileSystem
}
func NewStorage(forFile string, logger *zap.SugaredLogger, glob func(string) ([]string, error)) *Storage {
func NewStorage(forFile string, logger *zap.SugaredLogger, fs *filesystem.FileSystem) *Storage {
return &Storage{
FilePath: forFile,
basePath: filepath.Dir(forFile),
logger: logger,
glob: glob,
fs: fs,
}
}
@ -36,14 +36,14 @@ func (st *Storage) resolveFile(missingFileHandler *string, tpe, path string) ([]
var files []string
var err error
if remote.IsRemote(path) {
r := remote.NewRemote(st.logger, "", st.readFile, directoryExistsAt, fileExistsAt)
r := remote.NewRemote(st.logger, "", st.fs)
fetchedFilePath, err := r.Fetch(path, "values")
if err != nil {
return nil, false, err
}
if fileExistsAt(fetchedFilePath) {
if st.fs.FileExistsAt(fetchedFilePath) {
files = []string{fetchedFilePath}
}
} else {
@ -92,7 +92,7 @@ func (st *Storage) resolveFile(missingFileHandler *string, tpe, path string) ([]
func (st *Storage) ExpandPaths(globPattern string) ([]string, error) {
result := []string{}
absPathPattern := st.normalizePath(globPattern)
matches, err := st.glob(absPathPattern)
matches, err := st.fs.Glob(absPathPattern)
if err != nil {
return nil, fmt.Errorf("failed processing %s: %v", globPattern, err)
}

View File

@ -3,10 +3,10 @@ package state
import (
"fmt"
"os"
"path/filepath"
"reflect"
"testing"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/remote"
)
@ -73,7 +73,7 @@ func TestStorage_resolveFile(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
st := NewStorage(cacheDir, helmexec.NewLogger(os.Stderr, "debug"), filepath.Glob)
st := NewStorage(cacheDir, helmexec.NewLogger(os.Stderr, "debug"), filesystem.DefaultFileSystem())
files, skipped, err := st.resolveFile(tt.args.missingFileHandler, tt.args.title, tt.args.path)
if (err != nil) != tt.wantErr {

View File

@ -5,6 +5,8 @@ import (
"os"
"path/filepath"
"strings"
ffs "github.com/helmfile/helmfile/pkg/filesystem"
)
type TestFs struct {
@ -13,6 +15,7 @@ type TestFs struct {
files map[string]string
GlobFixtures map[string][]string
DeleteFile func(string) error
fileReaderCalls int
successfulReads []string
@ -34,9 +37,26 @@ func NewTestFs(files map[string]string) *TestFs {
successfulReads: []string{},
GlobFixtures: map[string][]string{},
DeleteFile: func(string) (ret error) { return },
}
}
func (f *TestFs) ToFileSystem() *ffs.FileSystem {
curfs := ffs.FileSystem{
FileExistsAt: f.FileExistsAt,
FileExists: f.FileExists,
DirectoryExistsAt: f.DirectoryExistsAt,
ReadFile: f.ReadFile,
Glob: f.Glob,
Getwd: f.Getwd,
Chdir: f.Chdir,
Abs: f.Abs,
DeleteFile: f.DeleteFile,
}
trfs := ffs.FromFileSystem(curfs)
return trfs
}
func (f *TestFs) FileExistsAt(path string) bool {
var ok bool
if strings.HasPrefix(path, "/") {

View File

@ -1,12 +1,13 @@
package tmpl
import "io/fs"
import (
"github.com/helmfile/helmfile/pkg/filesystem"
)
type Context struct {
preRender bool
basePath string
readFile func(string) ([]byte, error)
readDir func(string) ([]fs.DirEntry, error)
fs *filesystem.FileSystem
}
// SetBasePath sets the base path for the template
@ -14,10 +15,6 @@ func (c *Context) SetBasePath(path string) {
c.basePath = path
}
func (c *Context) SetReadFile(f func(string) ([]byte, error)) {
c.readFile = f
}
func (c *Context) SetReadDir(f func(string) ([]fs.DirEntry, error)) {
c.readDir = f
func (c *Context) SetFileSystem(fs *filesystem.FileSystem) {
c.fs = fs
}

View File

@ -220,11 +220,11 @@ func (c *Context) ReadFile(filename string) (string, error) {
path = filepath.Join(c.basePath, filename)
}
if c.readFile == nil {
if c.fs.ReadFile == nil {
return "", fmt.Errorf("readFile is not implemented")
}
bytes, err := c.readFile(path)
bytes, err := c.fs.ReadFile(path)
if err != nil {
return "", err
}
@ -239,7 +239,7 @@ func (c *Context) ReadDir(path string) ([]string, error) {
contextPath = filepath.Join(c.basePath, path)
}
entries, err := c.readDir(contextPath)
entries, err := c.fs.ReadDir(contextPath)
if err != nil {
return nil, fmt.Errorf("ReadDir %q: %w", contextPath, err)
}
@ -262,7 +262,7 @@ func (c *Context) ReadDirEntries(path string) ([]fs.DirEntry, error) {
} else {
contextPath = filepath.Join(c.basePath, path)
}
entries, err := c.readDir(contextPath)
entries, err := c.fs.ReadDir(contextPath)
if err != nil {
return nil, fmt.Errorf("ReadDirEntries %q: %w", contextPath, err)
}

View File

@ -9,6 +9,8 @@ import (
"testing"
"github.com/stretchr/testify/require"
"github.com/helmfile/helmfile/pkg/filesystem"
)
func TestCreateFuncMap(t *testing.T) {
@ -62,17 +64,23 @@ func TestCreateFuncMap_SkipInsecureTemplateFunctions(t *testing.T) {
skipInsecureTemplateFunctions = currentVal
}
func newFSExpecting(expectedFilename string, expected string) *filesystem.FileSystem {
return filesystem.FromFileSystem(filesystem.FileSystem{
ReadFile: func(filename string) ([]byte, error) {
if filename != expectedFilename {
return nil, fmt.Errorf("unexpected filename: expected=%v, actual=%s", expectedFilename, filename)
}
return []byte(expected), nil
},
})
}
func TestReadFile(t *testing.T) {
expected := `foo:
bar: BAR
`
expectedFilename := "values.yaml"
ctx := &Context{basePath: ".", readFile: func(filename string) ([]byte, error) {
if filename != expectedFilename {
return nil, fmt.Errorf("unexpected filename: expected=%v, actual=%s", expectedFilename, filename)
}
return []byte(expected), nil
}}
ctx := &Context{basePath: ".", fs: newFSExpecting(expectedFilename, expected)}
actual, err := ctx.ReadFile(expectedFilename)
require.NoError(t, err)
require.Equal(t, expected, actual)
@ -125,12 +133,12 @@ func TestReadDir(t *testing.T) {
}
expectedDirname := "sampleDirectory"
ctx := &Context{basePath: ".", readDir: func(dirname string) ([]fs.DirEntry, error) {
ctx := &Context{basePath: ".", fs: filesystem.FromFileSystem(filesystem.FileSystem{ReadDir: func(dirname string) ([]fs.DirEntry, error) {
if dirname != expectedDirname {
return nil, fmt.Errorf("unexpected filename: expected=%v, actual=%s", expectedDirname, dirname)
}
return result, nil
}}
}})}
actual, err := ctx.ReadDir(expectedDirname)
require.NoError(t, err)
@ -146,12 +154,12 @@ func TestReadDirEntries(t *testing.T) {
}
expectedDirname := "sampleDirectory"
ctx := &Context{basePath: ".", readDir: func(dirname string) ([]fs.DirEntry, error) {
ctx := &Context{basePath: ".", fs: filesystem.FromFileSystem(filesystem.FileSystem{ReadDir: func(dirname string) ([]fs.DirEntry, error) {
if dirname != expectedDirname {
return nil, fmt.Errorf("unexpected filename: expected=%v, actual=%s", expectedDirname, dirname)
}
return result, nil
}}
}})}
actual, err := ctx.ReadDirEntries(expectedDirname)
require.NoError(t, err)
@ -163,12 +171,7 @@ func TestReadFile_PassAbsPath(t *testing.T) {
bar: BAR
`
expectedFilename, _ := filepath.Abs("values.yaml")
ctx := &Context{basePath: ".", readFile: func(filename string) ([]byte, error) {
if filename != expectedFilename {
return nil, fmt.Errorf("unexpected filename: expected=%v, actual=%s", expectedFilename, filename)
}
return []byte(expected), nil
}}
ctx := &Context{basePath: ".", fs: newFSExpecting(expectedFilename, expected)}
actual, err := ctx.ReadFile(expectedFilename)
require.NoError(t, err)
require.Equal(t, actual, expected)

View File

@ -4,6 +4,8 @@ import (
"fmt"
"reflect"
"testing"
ffs "github.com/helmfile/helmfile/pkg/filesystem"
)
func TestRenderTemplate_Values(t *testing.T) {
@ -14,12 +16,12 @@ func TestRenderTemplate_Values(t *testing.T) {
bar: FOO_BAR
`
expectedFilename := "values.yaml"
ctx := &Context{readFile: func(filename string) ([]byte, error) {
ctx := &Context{fs: &ffs.FileSystem{ReadFile: func(filename string) ([]byte, error) {
if filename != expectedFilename {
return nil, fmt.Errorf("unexpected filename: expected=%v, actual=%s", expectedFilename, filename)
}
return []byte(valuesYamlContent), nil
}}
}}}
buf, err := ctx.RenderTemplateToBuffer(`{{ readFile "values.yaml" | fromYaml | setValueAtPath "foo.bar" "FOO_BAR" | toYaml }}`)
if err != nil {
t.Errorf("unexpected error: %v", err)
@ -43,12 +45,12 @@ func TestRenderTemplate_WithData(t *testing.T) {
"bar": "FOO_BAR",
},
}
ctx := &Context{readFile: func(filename string) ([]byte, error) {
ctx := &Context{fs: &ffs.FileSystem{ReadFile: func(filename string) ([]byte, error) {
if filename != expectedFilename {
return nil, fmt.Errorf("unexpected filename: expected=%v, actual=%s", expectedFilename, filename)
}
return []byte(valuesYamlContent), nil
}}
}}}
buf, err := ctx.RenderTemplateToBuffer(valuesYamlContent, data)
if err != nil {
t.Errorf("unexpected error: %v", err)
@ -68,12 +70,12 @@ func TestRenderTemplate_AccessingMissingKeyWithGetOrNil(t *testing.T) {
`
expectedFilename := "values.yaml"
data := map[string]interface{}{}
ctx := &Context{readFile: func(filename string) ([]byte, error) {
ctx := &Context{fs: &ffs.FileSystem{ReadFile: func(filename string) ([]byte, error) {
if filename != expectedFilename {
return nil, fmt.Errorf("unexpected filename: expected=%v, actual=%s", expectedFilename, filename)
}
return []byte(valuesYamlContent), nil
}}
}}}
buf, err := ctx.RenderTemplateToBuffer(valuesYamlContent, data)
if err != nil {
t.Errorf("unexpected error: %v", err)
@ -93,12 +95,12 @@ func TestRenderTemplate_Defaulting(t *testing.T) {
`
expectedFilename := "values.yaml"
data := map[string]interface{}{}
ctx := &Context{readFile: func(filename string) ([]byte, error) {
ctx := &Context{fs: &ffs.FileSystem{ReadFile: func(filename string) ([]byte, error) {
if filename != expectedFilename {
return nil, fmt.Errorf("unexpected filename: expected=%v, actual=%s", expectedFilename, filename)
}
return []byte(valuesYamlContent), nil
}}
}}}
buf, err := ctx.RenderTemplateToBuffer(valuesYamlContent, data)
if err != nil {
t.Errorf("unexpected error: %v", err)
@ -110,9 +112,9 @@ func TestRenderTemplate_Defaulting(t *testing.T) {
}
func renderTemplateToString(s string, data ...interface{}) (string, error) {
ctx := &Context{readFile: func(filename string) ([]byte, error) {
ctx := &Context{fs: &ffs.FileSystem{ReadFile: func(filename string) ([]byte, error) {
return nil, fmt.Errorf("unexpected call to readFile: filename=%s", filename)
}}
}}}
tplString, err := ctx.RenderTemplateToBuffer(s, data...)
if err != nil {
return "", err

View File

@ -3,43 +3,43 @@ package tmpl
import (
"bytes"
"fmt"
"os"
"strings"
"github.com/helmfile/helmfile/pkg/filesystem"
)
type FileRenderer struct {
ReadFile func(string) ([]byte, error)
Context *Context
Data interface{}
fs *filesystem.FileSystem
Context *Context
Data interface{}
}
func NewFileRenderer(readFile func(filename string) ([]byte, error), basePath string, data interface{}) *FileRenderer {
func NewFileRenderer(fs *filesystem.FileSystem, basePath string, data interface{}) *FileRenderer {
return &FileRenderer{
ReadFile: readFile,
fs: fs,
Context: &Context{
basePath: basePath,
readFile: readFile,
readDir: os.ReadDir,
fs: fs,
},
Data: data,
}
}
func NewFirstPassRenderer(basePath string, data interface{}) *FileRenderer {
fs := filesystem.DefaultFileSystem()
return &FileRenderer{
ReadFile: os.ReadFile,
fs: fs,
Context: &Context{
preRender: true,
basePath: basePath,
readFile: os.ReadFile,
readDir: os.ReadDir,
fs: fs,
},
Data: data,
}
}
func (r *FileRenderer) RenderTemplateFileToBuffer(file string) (*bytes.Buffer, error) {
content, err := r.ReadFile(file)
content, err := r.fs.ReadFile(file)
if err != nil {
return nil, err
}
@ -60,7 +60,7 @@ func (r *FileRenderer) RenderToBytes(path string) ([]byte, error) {
yamlBytes = yamlBuf.Bytes()
} else {
var err error
yamlBytes, err = r.ReadFile(path)
yamlBytes, err = r.fs.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to load [%s]: %v", path, err)
}

View File

@ -6,6 +6,7 @@ import (
"testing"
"github.com/helmfile/helmfile/pkg/environment"
"github.com/helmfile/helmfile/pkg/filesystem"
)
var emptyEnvTmplData = map[string]interface{}{
@ -23,7 +24,7 @@ func TestRenderToBytes_Gotmpl(t *testing.T) {
`
dataFile := "data.txt"
valuesTmplFile := "values.yaml.gotmpl"
r := NewFileRenderer(func(filename string) ([]byte, error) {
r := NewFileRenderer(&filesystem.FileSystem{ReadFile: func(filename string) ([]byte, error) {
switch filename {
case valuesTmplFile:
return []byte(valuesYamlTmplContent), nil
@ -31,7 +32,7 @@ func TestRenderToBytes_Gotmpl(t *testing.T) {
return []byte(dataFileContent), nil
}
return nil, fmt.Errorf("unexpected filename: expected=%v or %v, actual=%s", dataFile, valuesTmplFile, filename)
}, "", emptyEnvTmplData)
}}, "", emptyEnvTmplData)
buf, err := r.RenderToBytes(valuesTmplFile)
if err != nil {
t.Errorf("unexpected error: %v", err)
@ -50,12 +51,12 @@ func TestRenderToBytes_Yaml(t *testing.T) {
bar: '{{ readFile "data.txt" }}'
`
valuesFile := "values.yaml"
r := NewFileRenderer(func(filename string) ([]byte, error) {
r := NewFileRenderer(&filesystem.FileSystem{ReadFile: func(filename string) ([]byte, error) {
if filename == valuesFile {
return []byte(valuesYamlContent), nil
}
return nil, fmt.Errorf("unexpected filename: expected=%v, actual=%s", valuesFile, filename)
}, "", emptyEnvTmplData)
}}, "", emptyEnvTmplData)
buf, err := r.RenderToBytes(valuesFile)
if err != nil {
t.Errorf("unexpected error: %v", err)

View File

@ -1,5 +1,9 @@
package tmpl
import (
"github.com/helmfile/helmfile/pkg/filesystem"
)
type templateTextRenderer struct {
ReadText func(string) ([]byte, error)
Context *Context
@ -11,12 +15,12 @@ type TextRenderer interface {
}
// nolint: golint
func NewTextRenderer(readFile func(filename string) ([]byte, error), basePath string, data interface{}) *templateTextRenderer {
func NewTextRenderer(fs *filesystem.FileSystem, basePath string, data interface{}) *templateTextRenderer {
return &templateTextRenderer{
ReadText: readFile,
ReadText: fs.ReadFile,
Context: &Context{
basePath: basePath,
readFile: readFile,
fs: fs,
},
Data: data,
}

View File

@ -1,10 +1,11 @@
package tmpl
import (
"os"
"testing"
"github.com/stretchr/testify/require"
"github.com/helmfile/helmfile/pkg/filesystem"
)
// TestTextRenderer tests the text renderer.
@ -12,7 +13,7 @@ func TestNewTextRenderer(t *testing.T) {
tData := map[string]interface{}{
"foo": "bar",
}
tr := NewTextRenderer(os.ReadFile, ".", tData)
tr := NewTextRenderer(filesystem.DefaultFileSystem(), ".", tData)
require.Equal(t, tData, tr.Data)
require.Equal(t, ".", tr.Context.basePath)
}
@ -22,7 +23,7 @@ func TestTextRender(t *testing.T) {
tData := map[string]interface{}{
"foot": "bart",
}
tr := NewTextRenderer(os.ReadFile, ".", tData)
tr := NewTextRenderer(filesystem.DefaultFileSystem(), ".", tData)
tests := []struct {
text string

View File

@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/tmpl"
)
@ -276,7 +277,7 @@ func TestFileRendering(t *testing.T) {
filename := fmt.Sprintf("%s/%s.gotmpl", tempDir, tc.name)
os.WriteFile(filename, []byte(tc.tmplString), 0644)
fileRenderer := tmpl.NewFileRenderer(os.ReadFile, ".", tc.data)
fileRenderer := tmpl.NewFileRenderer(filesystem.DefaultFileSystem(), ".", tc.data)
tmpl_bytes, err := fileRenderer.RenderToBytes(filename)
if tc.wantErr {
@ -294,8 +295,7 @@ func TestFileRendering(t *testing.T) {
func TestTmplStrings(t *testing.T) {
c := &tmpl.Context{}
c.SetBasePath(".")
c.SetReadFile(os.ReadFile)
c.SetReadDir(os.ReadDir)
c.SetFileSystem(filesystem.DefaultFileSystem())
tmpl := template.New("stringTemplateTest").Funcs(c.CreateFuncMap())
tmplE2eTest.load()