helmfile/pkg/app/app_list_test.go

300 lines
8.5 KiB
Go

package app
import (
"bytes"
"os"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/helmfile/vals"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
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"
)
func testListWithEnvironment(t *testing.T, cfg configImpl) {
type testcase struct {
environment string
ns string
error string
selectors []string
expected string
}
check := func(t *testing.T, tc testcase, cfg configImpl) {
t.Helper()
bs := runWithLogCapture(t, "debug", func(t *testing.T, logger *zap.SugaredLogger) {
t.Helper()
valsRuntime, err := vals.New(vals.Options{CacheSize: 32})
if err != nil {
t.Errorf("unexpected error creating vals runtime: %v", err)
}
files := map[string]string{
"/path/to/helmfile.d/helmfile_1.yaml": `
environments:
development: {}
shared: {}
---
releases:
- name: logging
chart: incubator/raw
namespace: kube-system
- name: kubernetes-external-secrets
chart: incubator/raw
namespace: kube-system
needs:
- kube-system/logging
- name: external-secrets
chart: incubator/raw
namespace: default
labels:
app: test
needs:
- kube-system/kubernetes-external-secrets
- name: my-release
chart: incubator/raw
namespace: default
labels:
app: test
needs:
- default/external-secrets
# Disabled releases are treated as missing
- name: disabled
chart: incubator/raw
namespace: kube-system
installed: false
- name: test2
chart: incubator/raw
needs:
- kube-system/disabled
- name: test3
chart: incubator/raw
needs:
- test2
`,
"/path/to/helmfile.d/helmfile_2.yaml": `
environments:
test: {}
shared: {}
---
repositories:
- name: bitnami
url: https://charts.bitnami.com/bitnami
releases:
- name: cache
namespace: my-app
chart: bitnami/redis
version: 17.0.7
labels:
app: test
- name: database
namespace: my-app
chart: bitnami/postgres
version: 11.6.22
`,
"/path/to/helmfile.d/helmfile_3.yaml": `
releases:
- name: global
chart: incubator/raw
namespace: kube-system
`,
}
app := appWithFs(&App{
OverrideHelmBinary: DefaultHelmBinary,
fs: ffs.DefaultFileSystem(),
OverrideKubeContext: "default",
Env: tc.environment,
Logger: logger,
valsRuntime: valsRuntime,
}, files)
expectNoCallsToHelm(app)
if tc.ns != "" {
app.Namespace = tc.ns
}
if tc.selectors != nil {
app.Selectors = tc.selectors
}
var listErr error
out, err := testutil.CaptureStdout(func() {
listErr = app.ListReleases(cfg)
})
assert.NoError(t, err)
var gotErr string
if listErr != nil {
gotErr = listErr.Error()
}
if d := cmp.Diff(tc.error, gotErr); d != "" {
t.Fatalf("unexpected error: want (-), got (+): %s", d)
}
assert.Equal(t, tc.expected, out)
})
testhelper.RequireLog(t, "app_list_test", bs)
}
t.Run("default environment includes all releases", func(t *testing.T) {
check(t, testcase{
environment: "default",
expected: `NAME NAMESPACE ENABLED INSTALLED LABELS CHART VERSION
logging kube-system true true incubator/raw
kubernetes-external-secrets kube-system true true incubator/raw
external-secrets default true true app:test incubator/raw
my-release default true true app:test incubator/raw
disabled kube-system true false incubator/raw
test2 true true incubator/raw
test3 true true incubator/raw
cache my-app true true app:test bitnami/redis 17.0.7
database my-app true true bitnami/postgres 11.6.22
global kube-system true true incubator/raw
`,
}, cfg)
})
t.Run("fail on unknown environment", func(t *testing.T) {
check(t, testcase{
environment: "staging",
error: `err: no releases found that matches specified selector() and environment(staging), in any helmfile`,
}, cfg)
})
t.Run("list releases matching selector and environment", func(t *testing.T) {
check(t, testcase{
environment: "development",
selectors: []string{"app=test"},
expected: `NAME NAMESPACE ENABLED INSTALLED LABELS CHART VERSION
external-secrets default true true app:test incubator/raw
my-release default true true app:test incubator/raw
`,
}, cfg)
})
t.Run("filters releases for environment used in one file only", func(t *testing.T) {
check(t, testcase{
environment: "test",
expected: `NAME NAMESPACE ENABLED INSTALLED LABELS CHART VERSION
cache my-app true true app:test bitnami/redis 17.0.7
database my-app true true bitnami/postgres 11.6.22
`,
}, cfg)
})
t.Run("filters releases for environment used in multiple files", func(t *testing.T) {
check(t, testcase{
environment: "shared",
// 'global' release has no environments, so is still excluded
expected: `NAME NAMESPACE ENABLED INSTALLED LABELS CHART VERSION
logging kube-system true true incubator/raw
kubernetes-external-secrets kube-system true true incubator/raw
external-secrets default true true app:test incubator/raw
my-release default true true app:test incubator/raw
disabled kube-system true false incubator/raw
test2 true true incubator/raw
test3 true true incubator/raw
cache my-app true true app:test bitnami/redis 17.0.7
database my-app true true bitnami/postgres 11.6.22
`,
}, cfg)
})
}
func TestListWithEnvironment(t *testing.T) {
t.Run("with skipCharts=false", func(t *testing.T) {
testListWithEnvironment(t, configImpl{skipCharts: false})
})
t.Run("with skipCharts=true", func(t *testing.T) {
testListWithEnvironment(t, configImpl{skipCharts: true})
})
}
func testListWithJSONOutput(t *testing.T, cfg configImpl) {
cfg.output = "json"
files := map[string]string{
"/path/to/helmfile.d/first.yaml": `
environments:
default:
values:
- myrelease2:
enabled: false
releases:
- name: myrelease1
chart: mychart1
installed: false
labels:
id: myrelease1
- name: myrelease2
chart: mychart1
condition: myrelease2.enabled
`,
"/path/to/helmfile.d/second.yaml": `
releases:
- name: myrelease3
chart: mychart1
installed: true
- name: myrelease4
chart: mychart1
labels:
id: myrelease1
`,
}
stdout := os.Stdout
defer func() { os.Stdout = stdout }()
var buffer bytes.Buffer
logger := helmexec.NewLogger(&buffer, "debug")
app := appWithFs(&App{
OverrideHelmBinary: DefaultHelmBinary,
fs: ffs.DefaultFileSystem(),
OverrideKubeContext: "default",
Env: "default",
Logger: logger,
Namespace: "testNamespace",
}, files)
expectNoCallsToHelm(app)
out, err := testutil.CaptureStdout(func() {
err := app.ListReleases(cfg)
assert.Nil(t, err)
})
assert.NoError(t, err)
expected := `[{"name":"myrelease1","namespace":"testNamespace","enabled":true,"installed":false,"labels":"id:myrelease1","chart":"mychart1","version":""},{"name":"myrelease2","namespace":"testNamespace","enabled":false,"installed":true,"labels":"","chart":"mychart1","version":""},{"name":"myrelease3","namespace":"testNamespace","enabled":true,"installed":true,"labels":"","chart":"mychart1","version":""},{"name":"myrelease4","namespace":"testNamespace","enabled":true,"installed":true,"labels":"id:myrelease1","chart":"mychart1","version":""}]
`
assert.Equal(t, expected, out)
}
func TestListWithJSONOutput(t *testing.T) {
t.Run("with skipCharts=false", func(t *testing.T) {
testListWithJSONOutput(t, configImpl{skipCharts: false})
})
t.Run("with skipCharts=true", func(t *testing.T) {
testListWithJSONOutput(t, configImpl{skipCharts: true})
})
}