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")
 | 
						|
	}
 | 
						|
}
 |