fix: Relative path in helmfile not relative to file in multi file setup (#448)
Fixes #431
This commit is contained in:
parent
99ce8570c7
commit
bf42d2519d
355
app_test.go
355
app_test.go
|
|
@ -8,20 +8,122 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testFs struct {
|
||||
wd string
|
||||
dirs map[string]bool
|
||||
files map[string]string
|
||||
}
|
||||
|
||||
func appWithFs(app *app, files map[string]string) *app {
|
||||
fs := newTestFs(files)
|
||||
return injectFs(app, fs)
|
||||
}
|
||||
|
||||
func injectFs(app *app, fs *testFs) *app {
|
||||
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.directoryExistsAt = fs.directoryExistsAt
|
||||
return app
|
||||
}
|
||||
|
||||
func newTestFs(files map[string]string) *testFs {
|
||||
dirs := map[string]bool{}
|
||||
for abs, _ := range files {
|
||||
d := filepath.Dir(abs)
|
||||
dirs[d] = true
|
||||
}
|
||||
return &testFs{
|
||||
wd: "/path/to",
|
||||
dirs: dirs,
|
||||
files: files,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *testFs) fileExistsAt(path string) bool {
|
||||
var ok bool
|
||||
if strings.Contains(path, "/") {
|
||||
_, ok = f.files[path]
|
||||
} else {
|
||||
_, ok = f.files[filepath.Join(f.wd, path)]
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
func (f *testFs) directoryExistsAt(path string) bool {
|
||||
var ok bool
|
||||
if strings.Contains(path, "/") {
|
||||
_, ok = f.dirs[path]
|
||||
} else {
|
||||
_, ok = f.dirs[filepath.Join(f.wd, path)]
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
func (f *testFs) readFile(filename string) ([]byte, error) {
|
||||
var str string
|
||||
var ok bool
|
||||
if strings.Contains(filename, "/") {
|
||||
str, ok = f.files[filename]
|
||||
} else {
|
||||
str, ok = f.files[filepath.Join(f.wd, filename)]
|
||||
}
|
||||
if !ok {
|
||||
return []byte(nil), fmt.Errorf("no file found: %s", filename)
|
||||
}
|
||||
return []byte(str), nil
|
||||
}
|
||||
|
||||
func (f *testFs) glob(pattern string) ([]string, error) {
|
||||
matches := []string{}
|
||||
for name, _ := range f.files {
|
||||
matched, err := filepath.Match(pattern, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if matched {
|
||||
matches = append(matches, name)
|
||||
}
|
||||
}
|
||||
if len(matches) == 0 {
|
||||
return []string(nil), fmt.Errorf("no file matched: %s", pattern)
|
||||
}
|
||||
return matches, nil
|
||||
}
|
||||
|
||||
func (f *testFs) abs(path string) (string, error) {
|
||||
var p string
|
||||
if path[0] == '/' {
|
||||
p = path
|
||||
} else {
|
||||
p = filepath.Join(f.wd, path)
|
||||
}
|
||||
return filepath.Clean(p), nil
|
||||
}
|
||||
|
||||
func (f *testFs) getwd() (string, error) {
|
||||
return f.wd, nil
|
||||
}
|
||||
|
||||
func (f *testFs) chdir(dir string) error {
|
||||
if dir == "/path/to" || dir == "/path/to/helmfile.d" {
|
||||
f.wd = dir
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("unexpected chdir \"%s\"", dir)
|
||||
}
|
||||
|
||||
// See https://github.com/roboll/helmfile/issues/193
|
||||
func TestVisitDesiredStatesWithReleasesFiltered(t *testing.T) {
|
||||
absPaths := map[string]string{
|
||||
".": "/path/to",
|
||||
"/path/to/helmfile.d": "/path/to/helmfile.d",
|
||||
}
|
||||
dirs := map[string]bool{
|
||||
"helmfile.d": true,
|
||||
}
|
||||
files := map[string]string{
|
||||
"helmfile.yaml": `
|
||||
"/path/to/helmfile.yaml": `
|
||||
helmfiles:
|
||||
- helmfile.d/a*.yaml
|
||||
- helmfile.d/b*.yaml
|
||||
|
|
@ -42,39 +144,6 @@ releases:
|
|||
chart: stable/grafana
|
||||
`,
|
||||
}
|
||||
globMatches := map[string][]string{
|
||||
"/path/to/helmfile.d/a*.yaml": []string{"/path/to/helmfile.d/a1.yaml", "/path/to/helmfile.d/a2.yaml"},
|
||||
"/path/to/helmfile.d/b*.yaml": []string{"/path/to/helmfile.d/b.yaml"},
|
||||
}
|
||||
fileExistsAt := func(path string) bool {
|
||||
_, ok := files[path]
|
||||
return ok
|
||||
}
|
||||
directoryExistsAt := func(path string) bool {
|
||||
_, ok := dirs[path]
|
||||
return ok
|
||||
}
|
||||
readFile := func(filename string) ([]byte, error) {
|
||||
str, ok := files[filename]
|
||||
if !ok {
|
||||
return []byte(nil), fmt.Errorf("no file found: %s", filename)
|
||||
}
|
||||
return []byte(str), nil
|
||||
}
|
||||
glob := func(pattern string) ([]string, error) {
|
||||
matches, ok := globMatches[pattern]
|
||||
if !ok {
|
||||
return []string(nil), fmt.Errorf("no file matched: %s", pattern)
|
||||
}
|
||||
return matches, nil
|
||||
}
|
||||
abs := func(path string) (string, error) {
|
||||
a, ok := absPaths[path]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("abs: unexpected path: %s", path)
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
noop := func(st *state.HelmState, helm helmexec.Interface) []error {
|
||||
return []error{}
|
||||
}
|
||||
|
|
@ -90,18 +159,13 @@ releases:
|
|||
}
|
||||
|
||||
for _, testcase := range testcases {
|
||||
app := &app{
|
||||
readFile: readFile,
|
||||
glob: glob,
|
||||
abs: abs,
|
||||
fileExistsAt: fileExistsAt,
|
||||
directoryExistsAt: directoryExistsAt,
|
||||
kubeContext: "default",
|
||||
logger: helmexec.NewLogger(os.Stderr, "debug"),
|
||||
selectors: []string{fmt.Sprintf("name=%s", testcase.name)},
|
||||
namespace: "",
|
||||
env: "default",
|
||||
}
|
||||
app := appWithFs(&app{
|
||||
kubeContext: "default",
|
||||
logger: helmexec.NewLogger(os.Stderr, "debug"),
|
||||
selectors: []string{fmt.Sprintf("name=%s", testcase.name)},
|
||||
namespace: "",
|
||||
env: "default",
|
||||
}, files)
|
||||
err := app.VisitDesiredStatesWithReleasesFiltered(
|
||||
"helmfile.yaml", noop,
|
||||
)
|
||||
|
|
@ -115,15 +179,8 @@ releases:
|
|||
|
||||
// See https://github.com/roboll/helmfile/issues/320
|
||||
func TestVisitDesiredStatesWithReleasesFiltered_UndefinedEnv(t *testing.T) {
|
||||
absPaths := map[string]string{
|
||||
".": "/path/to",
|
||||
"/path/to/helmfile.d": "/path/to/helmfile.d",
|
||||
}
|
||||
dirs := map[string]bool{
|
||||
"helmfile.d": true,
|
||||
}
|
||||
files := map[string]string{
|
||||
"helmfile.yaml": `
|
||||
"/path/to/helmfile.yaml": `
|
||||
environments:
|
||||
prod:
|
||||
|
||||
|
|
@ -139,38 +196,6 @@ releases:
|
|||
chart: stable/zipkin
|
||||
`,
|
||||
}
|
||||
globMatches := map[string][]string{
|
||||
"/path/to/helmfile.d/a*.yaml": []string{"/path/to/helmfile.d/a1.yaml"},
|
||||
}
|
||||
fileExistsAt := func(path string) bool {
|
||||
_, ok := files[path]
|
||||
return ok
|
||||
}
|
||||
directoryExistsAt := func(path string) bool {
|
||||
_, ok := dirs[path]
|
||||
return ok
|
||||
}
|
||||
readFile := func(filename string) ([]byte, error) {
|
||||
str, ok := files[filename]
|
||||
if !ok {
|
||||
return []byte(nil), fmt.Errorf("no file found: %s", filename)
|
||||
}
|
||||
return []byte(str), nil
|
||||
}
|
||||
glob := func(pattern string) ([]string, error) {
|
||||
matches, ok := globMatches[pattern]
|
||||
if !ok {
|
||||
return []string(nil), fmt.Errorf("no file matched: %s", pattern)
|
||||
}
|
||||
return matches, nil
|
||||
}
|
||||
abs := func(path string) (string, error) {
|
||||
a, ok := absPaths[path]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("abs: unexpected path: %s", path)
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
noop := func(st *state.HelmState, helm helmexec.Interface) []error {
|
||||
return []error{}
|
||||
}
|
||||
|
|
@ -185,18 +210,13 @@ releases:
|
|||
}
|
||||
|
||||
for _, testcase := range testcases {
|
||||
app := &app{
|
||||
readFile: readFile,
|
||||
glob: glob,
|
||||
abs: abs,
|
||||
fileExistsAt: fileExistsAt,
|
||||
directoryExistsAt: directoryExistsAt,
|
||||
kubeContext: "default",
|
||||
logger: helmexec.NewLogger(os.Stderr, "debug"),
|
||||
namespace: "",
|
||||
selectors: []string{},
|
||||
env: testcase.name,
|
||||
}
|
||||
app := appWithFs(&app{
|
||||
kubeContext: "default",
|
||||
logger: helmexec.NewLogger(os.Stderr, "debug"),
|
||||
namespace: "",
|
||||
selectors: []string{},
|
||||
env: testcase.name,
|
||||
}, files)
|
||||
err := app.VisitDesiredStatesWithReleasesFiltered(
|
||||
"helmfile.yaml", noop,
|
||||
)
|
||||
|
|
@ -210,15 +230,8 @@ releases:
|
|||
|
||||
// See https://github.com/roboll/helmfile/issues/322
|
||||
func TestVisitDesiredStatesWithReleasesFiltered_Selectors(t *testing.T) {
|
||||
absPaths := map[string]string{
|
||||
".": "/path/to",
|
||||
"/path/to/helmfile.d": "/path/to/helmfile.d",
|
||||
}
|
||||
dirs := map[string]bool{
|
||||
"helmfile.d": true,
|
||||
}
|
||||
files := map[string]string{
|
||||
"helmfile.yaml": `
|
||||
"/path/to/helmfile.yaml": `
|
||||
helmfiles:
|
||||
- helmfile.d/a*.yaml
|
||||
- helmfile.d/b*.yaml
|
||||
|
|
@ -247,39 +260,6 @@ releases:
|
|||
duplicated: yes
|
||||
`,
|
||||
}
|
||||
globMatches := map[string][]string{
|
||||
"/path/to/helmfile.d/a*.yaml": []string{"/path/to/helmfile.d/a1.yaml", "/path/to/helmfile.d/a2.yaml"},
|
||||
"/path/to/helmfile.d/b*.yaml": []string{"/path/to/helmfile.d/b.yaml"},
|
||||
}
|
||||
fileExistsAt := func(path string) bool {
|
||||
_, ok := files[path]
|
||||
return ok
|
||||
}
|
||||
directoryExistsAt := func(path string) bool {
|
||||
_, ok := dirs[path]
|
||||
return ok
|
||||
}
|
||||
readFile := func(filename string) ([]byte, error) {
|
||||
str, ok := files[filename]
|
||||
if !ok {
|
||||
return []byte(nil), fmt.Errorf("no file found: %s", filename)
|
||||
}
|
||||
return []byte(str), nil
|
||||
}
|
||||
glob := func(pattern string) ([]string, error) {
|
||||
matches, ok := globMatches[pattern]
|
||||
if !ok {
|
||||
return []string(nil), fmt.Errorf("no file matched: %s", pattern)
|
||||
}
|
||||
return matches, nil
|
||||
}
|
||||
abs := func(path string) (string, error) {
|
||||
a, ok := absPaths[path]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("abs: unexpected path: %s", path)
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
testcases := []struct {
|
||||
label string
|
||||
|
|
@ -305,18 +285,13 @@ releases:
|
|||
return []error{}
|
||||
}
|
||||
|
||||
app := &app{
|
||||
readFile: readFile,
|
||||
glob: glob,
|
||||
abs: abs,
|
||||
fileExistsAt: fileExistsAt,
|
||||
directoryExistsAt: directoryExistsAt,
|
||||
kubeContext: "default",
|
||||
logger: helmexec.NewLogger(os.Stderr, "debug"),
|
||||
namespace: "",
|
||||
selectors: []string{testcase.label},
|
||||
env: "default",
|
||||
}
|
||||
app := appWithFs(&app{
|
||||
kubeContext: "default",
|
||||
logger: helmexec.NewLogger(os.Stderr, "debug"),
|
||||
namespace: "",
|
||||
selectors: []string{testcase.label},
|
||||
env: "default",
|
||||
}, files)
|
||||
|
||||
err := app.VisitDesiredStatesWithReleasesFiltered(
|
||||
"helmfile.yaml", collectReleases,
|
||||
|
|
@ -338,15 +313,8 @@ releases:
|
|||
|
||||
// See https://github.com/roboll/helmfile/issues/312
|
||||
func TestVisitDesiredStatesWithReleasesFiltered_ReverseOrder(t *testing.T) {
|
||||
absPaths := map[string]string{
|
||||
".": "/path/to",
|
||||
"/path/to/helmfile.d": "/path/to/helmfile.d",
|
||||
}
|
||||
dirs := map[string]bool{
|
||||
"helmfile.d": true,
|
||||
}
|
||||
files := map[string]string{
|
||||
"helmfile.yaml": `
|
||||
"/path/to/helmfile.yaml": `
|
||||
helmfiles:
|
||||
- helmfile.d/a*.yaml
|
||||
- helmfile.d/b*.yaml
|
||||
|
|
@ -369,39 +337,6 @@ releases:
|
|||
chart: stable/grafana
|
||||
`,
|
||||
}
|
||||
globMatches := map[string][]string{
|
||||
"/path/to/helmfile.d/a*.yaml": []string{"/path/to/helmfile.d/a1.yaml", "/path/to/helmfile.d/a2.yaml"},
|
||||
"/path/to/helmfile.d/b*.yaml": []string{"/path/to/helmfile.d/b.yaml"},
|
||||
}
|
||||
fileExistsAt := func(path string) bool {
|
||||
_, ok := files[path]
|
||||
return ok
|
||||
}
|
||||
directoryExistsAt := func(path string) bool {
|
||||
_, ok := dirs[path]
|
||||
return ok
|
||||
}
|
||||
readFile := func(filename string) ([]byte, error) {
|
||||
str, ok := files[filename]
|
||||
if !ok {
|
||||
return []byte(nil), fmt.Errorf("no file found: %s", filename)
|
||||
}
|
||||
return []byte(str), nil
|
||||
}
|
||||
glob := func(pattern string) ([]string, error) {
|
||||
matches, ok := globMatches[pattern]
|
||||
if !ok {
|
||||
return []string(nil), fmt.Errorf("no file matched: %s", pattern)
|
||||
}
|
||||
return matches, nil
|
||||
}
|
||||
abs := func(path string) (string, error) {
|
||||
a, ok := absPaths[path]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("abs: unexpected path: %s", path)
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
expected := []string{"grafana", "elasticsearch", "prometheus", "zipkin"}
|
||||
|
||||
|
|
@ -421,20 +356,14 @@ releases:
|
|||
}
|
||||
return []error{}
|
||||
}
|
||||
|
||||
app := &app{
|
||||
readFile: readFile,
|
||||
glob: glob,
|
||||
abs: abs,
|
||||
fileExistsAt: fileExistsAt,
|
||||
directoryExistsAt: directoryExistsAt,
|
||||
kubeContext: "default",
|
||||
logger: helmexec.NewLogger(os.Stderr, "debug"),
|
||||
reverse: testcase.reverse,
|
||||
namespace: "",
|
||||
selectors: []string{},
|
||||
env: "default",
|
||||
}
|
||||
app := appWithFs(&app{
|
||||
kubeContext: "default",
|
||||
logger: helmexec.NewLogger(os.Stderr, "debug"),
|
||||
reverse: testcase.reverse,
|
||||
namespace: "",
|
||||
selectors: []string{},
|
||||
env: "default",
|
||||
}, files)
|
||||
err := app.VisitDesiredStatesWithReleasesFiltered(
|
||||
"helmfile.yaml", collectReleases,
|
||||
)
|
||||
|
|
|
|||
91
main.go
91
main.go
|
|
@ -571,6 +571,9 @@ type app struct {
|
|||
env string
|
||||
namespace string
|
||||
selectors []string
|
||||
|
||||
getwd func() (string, error)
|
||||
chdir func(string) error
|
||||
}
|
||||
|
||||
func findAndIterateOverDesiredStatesUsingFlags(c *cli.Context, converge func(*state.HelmState, helmexec.Interface, context) []error) error {
|
||||
|
|
@ -598,6 +601,8 @@ func initAppEntry(c *cli.Context, reverse bool) (*app, string, error) {
|
|||
readFile: ioutil.ReadFile,
|
||||
glob: filepath.Glob,
|
||||
abs: filepath.Abs,
|
||||
getwd: os.Getwd,
|
||||
chdir: os.Chdir,
|
||||
fileExistsAt: fileExistsAt,
|
||||
directoryExistsAt: directoryExistsAt,
|
||||
kubeContext: kubeContext,
|
||||
|
|
@ -778,16 +783,69 @@ func (r *twoPassRenderer) renderTemplate(content []byte) (*bytes.Buffer, error)
|
|||
return yamlBuf, nil
|
||||
}
|
||||
|
||||
func (a *app) VisitDesiredStates(fileOrDir string, converge func(*state.HelmState, helmexec.Interface) (bool, []error)) error {
|
||||
func (a *app) within(dir string, do func() error) error {
|
||||
prev, err := a.getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed getting current working direcotyr: %v", err)
|
||||
}
|
||||
|
||||
absDir, err := a.abs(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.logger.Debugf("changing working directory to \"%s\"", absDir)
|
||||
|
||||
if err := a.chdir(absDir); err != nil {
|
||||
return fmt.Errorf("failed changing working directory to \"%s\": %v", absDir, err)
|
||||
}
|
||||
|
||||
appErr := do()
|
||||
|
||||
a.logger.Debugf("changing working directory back to \"%s\"", prev)
|
||||
|
||||
if chdirBackErr := a.chdir(prev); chdirBackErr != nil {
|
||||
if appErr != nil {
|
||||
a.logger.Warnf("%v", appErr)
|
||||
}
|
||||
return fmt.Errorf("failed chaging working directory back to \"%s\": %v", prev, chdirBackErr)
|
||||
}
|
||||
|
||||
return appErr
|
||||
}
|
||||
|
||||
func (a *app) visitStateFiles(fileOrDir string, do func(string) error) error {
|
||||
desiredStateFiles, err := a.findDesiredStateFiles(fileOrDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
noMatchInHelmfiles := true
|
||||
for _, f := range desiredStateFiles {
|
||||
a.logger.Debugf("Processing %s", f)
|
||||
for _, relPath := range desiredStateFiles {
|
||||
a.logger.Debugf("Processing %s", relPath)
|
||||
|
||||
var file string
|
||||
var dir string
|
||||
if a.directoryExistsAt(fileOrDir) {
|
||||
file = fileOrDir
|
||||
dir = fileOrDir
|
||||
} else {
|
||||
file = filepath.Base(fileOrDir)
|
||||
dir = filepath.Dir(fileOrDir)
|
||||
}
|
||||
err := a.within(dir, func() error {
|
||||
return do(file)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *app) VisitDesiredStates(fileOrDir string, converge func(*state.HelmState, helmexec.Interface) (bool, []error)) error {
|
||||
noMatchInHelmfiles := true
|
||||
err := a.visitStateFiles(fileOrDir, func(f string) error {
|
||||
content, err := a.readFile(f)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -821,7 +879,7 @@ func (a *app) VisitDesiredStates(fileOrDir string, converge func(*state.HelmStat
|
|||
case *state.StateLoadError:
|
||||
switch stateLoadErr.Cause.(type) {
|
||||
case *state.UndefinedEnvError:
|
||||
continue
|
||||
return nil
|
||||
default:
|
||||
return err
|
||||
}
|
||||
|
|
@ -859,9 +917,10 @@ func (a *app) VisitDesiredStates(fileOrDir string, converge func(*state.HelmStat
|
|||
noMatchInHelmfiles = noMatchInHelmfiles && !processed
|
||||
}
|
||||
|
||||
if err := clean(st, errs); err != nil {
|
||||
return err
|
||||
}
|
||||
return clean(st, errs)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if noMatchInHelmfiles {
|
||||
return &noMatchingHelmfileError{selectors: a.selectors, env: a.env}
|
||||
|
|
@ -902,6 +961,22 @@ func (a *app) VisitDesiredStatesWithReleasesFiltered(fileOrDir string, converge
|
|||
return nil
|
||||
}
|
||||
|
||||
func (a *app) findStateFilesInAbsPaths(specifiedPath string) ([]string, error) {
|
||||
rels, err := a.findDesiredStateFiles(specifiedPath)
|
||||
if err != nil {
|
||||
return rels, err
|
||||
}
|
||||
|
||||
files := make([]string, len(rels))
|
||||
for i := range rels {
|
||||
files[i], err = filepath.Abs(rels[i])
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func (a *app) findDesiredStateFiles(specifiedPath string) ([]string, error) {
|
||||
var helmfileDir string
|
||||
if specifiedPath != "" {
|
||||
|
|
|
|||
Loading…
Reference in New Issue