475 lines
12 KiB
Go
475 lines
12 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/roboll/helmfile/helmexec"
|
|
"github.com/roboll/helmfile/state"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
// 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": `
|
|
helmfiles:
|
|
- helmfile.d/a*.yaml
|
|
- helmfile.d/b*.yaml
|
|
`,
|
|
"/path/to/helmfile.d/a1.yaml": `
|
|
releases:
|
|
- name: zipkin
|
|
chart: stable/zipkin
|
|
`,
|
|
"/path/to/helmfile.d/a2.yaml": `
|
|
releases:
|
|
- name: prometheus
|
|
chart: stable/prometheus
|
|
`,
|
|
"/path/to/helmfile.d/b.yaml": `
|
|
releases:
|
|
- name: grafana
|
|
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{}
|
|
}
|
|
|
|
testcases := []struct {
|
|
name string
|
|
expectErr bool
|
|
}{
|
|
{name: "prometheus", expectErr: false},
|
|
{name: "zipkin", expectErr: false},
|
|
{name: "grafana", expectErr: false},
|
|
{name: "elasticsearch", expectErr: true},
|
|
}
|
|
|
|
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",
|
|
}
|
|
err := app.VisitDesiredStatesWithReleasesFiltered(
|
|
"helmfile.yaml", noop,
|
|
)
|
|
if testcase.expectErr && err == nil {
|
|
t.Errorf("error expected but not happened for name=%s", testcase.name)
|
|
} else if !testcase.expectErr && err != nil {
|
|
t.Errorf("unexpected error for name=%s: %v", testcase.name, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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": `
|
|
environments:
|
|
prod:
|
|
|
|
helmfiles:
|
|
- helmfile.d/a*.yaml
|
|
`,
|
|
"/path/to/helmfile.d/a1.yaml": `
|
|
environments:
|
|
prod:
|
|
|
|
releases:
|
|
- name: zipkin
|
|
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{}
|
|
}
|
|
|
|
testcases := []struct {
|
|
name string
|
|
expectErr bool
|
|
}{
|
|
{name: "undefined_env", expectErr: true},
|
|
{name: "default", expectErr: false},
|
|
{name: "prod", expectErr: false},
|
|
}
|
|
|
|
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,
|
|
}
|
|
err := app.VisitDesiredStatesWithReleasesFiltered(
|
|
"helmfile.yaml", noop,
|
|
)
|
|
if testcase.expectErr && err == nil {
|
|
t.Errorf("error expected but not happened for environment=%s", testcase.name)
|
|
} else if !testcase.expectErr && err != nil {
|
|
t.Errorf("unexpected error for environment=%s: %v", testcase.name, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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": `
|
|
helmfiles:
|
|
- helmfile.d/a*.yaml
|
|
- helmfile.d/b*.yaml
|
|
`,
|
|
"/path/to/helmfile.d/a1.yaml": `
|
|
releases:
|
|
- name: zipkin
|
|
chart: stable/zipkin
|
|
`,
|
|
"/path/to/helmfile.d/a2.yaml": `
|
|
releases:
|
|
- name: prometheus
|
|
chart: stable/prometheus
|
|
`,
|
|
"/path/to/helmfile.d/b.yaml": `
|
|
releases:
|
|
- name: grafana
|
|
chart: stable/grafana
|
|
- name: foo
|
|
chart: charts/foo
|
|
labels:
|
|
duplicated: yes
|
|
- name: foo
|
|
chart: charts/foo
|
|
labels:
|
|
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
|
|
expectedCount int
|
|
expectErr bool
|
|
errMsg string
|
|
}{
|
|
{label: "name=prometheus", expectedCount: 1, expectErr: false},
|
|
{label: "name=", expectedCount: 0, expectErr: true, errMsg: "failed processing /path/to/helmfile.d/a1.yaml: Malformed label: name=. Expected label in form k=v or k!=v"},
|
|
{label: "name!=", expectedCount: 0, expectErr: true, errMsg: "failed processing /path/to/helmfile.d/a1.yaml: Malformed label: name!=. Expected label in form k=v or k!=v"},
|
|
{label: "name", expectedCount: 0, expectErr: true, errMsg: "failed processing /path/to/helmfile.d/a1.yaml: Malformed label: name. Expected label in form k=v or k!=v"},
|
|
// See https://github.com/roboll/helmfile/issues/193
|
|
{label: "duplicated=yes", expectedCount: 0, expectErr: true, errMsg: "failed processing /path/to/helmfile.d/b.yaml: duplicate release \"foo\" found: there were 2 releases named \"foo\" matching specified selector"},
|
|
}
|
|
|
|
for _, testcase := range testcases {
|
|
actual := []string{}
|
|
|
|
collectReleases := func(st *state.HelmState, helm helmexec.Interface) []error {
|
|
for _, r := range st.Releases {
|
|
actual = append(actual, r.Name)
|
|
}
|
|
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",
|
|
}
|
|
|
|
err := app.VisitDesiredStatesWithReleasesFiltered(
|
|
"helmfile.yaml", collectReleases,
|
|
)
|
|
if testcase.expectErr {
|
|
if err == nil {
|
|
t.Errorf("error expected but not happened for selector %s", testcase.label)
|
|
} else if err.Error() != testcase.errMsg {
|
|
t.Errorf("unexpected error message: expected=\"%s\", actual=\"%s\"", testcase.errMsg, err.Error())
|
|
}
|
|
} else if !testcase.expectErr && err != nil {
|
|
t.Errorf("unexpected error for selector %s: %v", testcase.label, err)
|
|
}
|
|
if len(actual) != testcase.expectedCount {
|
|
t.Errorf("unexpected release count for selector %s: expected=%d, actual=%d", testcase.label, testcase.expectedCount, len(actual))
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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": `
|
|
helmfiles:
|
|
- helmfile.d/a*.yaml
|
|
- helmfile.d/b*.yaml
|
|
`,
|
|
"/path/to/helmfile.d/a1.yaml": `
|
|
releases:
|
|
- name: zipkin
|
|
chart: stable/zipkin
|
|
`,
|
|
"/path/to/helmfile.d/a2.yaml": `
|
|
releases:
|
|
- name: prometheus
|
|
chart: stable/prometheus
|
|
- name: elasticsearch
|
|
chart: stable/elasticsearch
|
|
`,
|
|
"/path/to/helmfile.d/b.yaml": `
|
|
releases:
|
|
- name: grafana
|
|
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"}
|
|
|
|
testcases := []struct {
|
|
reverse bool
|
|
expected []string
|
|
}{
|
|
{reverse: false, expected: []string{"zipkin", "prometheus", "elasticsearch", "grafana"}},
|
|
{reverse: true, expected: []string{"grafana", "elasticsearch", "prometheus", "zipkin"}},
|
|
}
|
|
for _, testcase := range testcases {
|
|
actual := []string{}
|
|
|
|
collectReleases := func(st *state.HelmState, helm helmexec.Interface) []error {
|
|
for _, r := range st.Releases {
|
|
actual = append(actual, r.Name)
|
|
}
|
|
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",
|
|
}
|
|
err := app.VisitDesiredStatesWithReleasesFiltered(
|
|
"helmfile.yaml", collectReleases,
|
|
)
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if !reflect.DeepEqual(testcase.expected, actual) {
|
|
t.Errorf("releases did not match: expected=%v actual=%v", expected, actual)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLoadDesiredStateFromYaml_DuplicateReleaseName(t *testing.T) {
|
|
yamlFile := "example/path/to/yaml/file"
|
|
yamlContent := []byte(`releases:
|
|
- name: myrelease1
|
|
chart: mychart1
|
|
labels:
|
|
stage: pre
|
|
foo: bar
|
|
- name: myrelease1
|
|
chart: mychart2
|
|
labels:
|
|
stage: post
|
|
`)
|
|
app := &app{
|
|
readFile: ioutil.ReadFile,
|
|
glob: filepath.Glob,
|
|
abs: filepath.Abs,
|
|
kubeContext: "default",
|
|
logger: logger,
|
|
}
|
|
_, err := app.loadDesiredStateFromYaml(yamlContent, yamlFile, "default", "default")
|
|
if err != nil {
|
|
t.Error("unexpected error")
|
|
}
|
|
}
|