fix: `helmfile destroy` should delete releases in the specified tiller namespace (#536)

Actually, 4 helm commands including "list", "diff", "status" and "delete" were not taking tiller-rerelated flags into account in helmfile. This fixes all these commands.

Fixes #534
This commit is contained in:
KUOKA Yusuke 2019-04-03 23:41:41 +09:00 committed by GitHub
parent f522ba320b
commit 0e00cdf2a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 220 additions and 34 deletions

View File

@ -103,16 +103,16 @@ func (helm *execer) SyncRelease(name, chart string, flags ...string) error {
return err
}
func (helm *execer) ReleaseStatus(name string) error {
func (helm *execer) ReleaseStatus(name string, flags ...string) error {
helm.logger.Infof("Getting status %v", name)
out, err := helm.exec(append([]string{"status", name})...)
out, err := helm.exec(append([]string{"status", name}, flags...)...)
helm.write(out)
return err
}
func (helm *execer) List(filter string) (string, error) {
func (helm *execer) List(filter string, flags ...string) (string, error) {
helm.logger.Infof("Listing releases matching %v", filter)
out, err := helm.exec(append([]string{"list", filter})...)
out, err := helm.exec(append([]string{"list", filter}, flags...)...)
helm.write(out)
return string(out), err
}

View File

@ -14,9 +14,9 @@ type Interface interface {
TemplateRelease(chart string, flags ...string) error
Fetch(chart string, flags ...string) error
Lint(chart string, flags ...string) error
ReleaseStatus(name string) error
ReleaseStatus(name string, flags ...string) error
DeleteRelease(name string, flags ...string) error
TestRelease(name string, flags ...string) error
List(filter string) (string, error)
List(filter string, flags ...string) (string, error)
DecryptSecret(name string) (string, error)
}

View File

@ -277,8 +277,8 @@ func (st *HelmState) prepareSyncReleases(helm helmexec.Interface, additionalValu
return res, errs
}
func isReleaseInstalled(helm helmexec.Interface, release ReleaseSpec) (bool, error) {
out, err := helm.List("^" + release.Name + "$")
func (st *HelmState) isReleaseInstalled(helm helmexec.Interface, release ReleaseSpec) (bool, error) {
out, err := helm.List("^"+release.Name+"$", st.tillerFlags(&release)...)
if err != nil {
return false, err
} else if out != "" {
@ -292,7 +292,7 @@ func (st *HelmState) DetectReleasesToBeDeleted(helm helmexec.Interface) ([]*Rele
for i, _ := range st.Releases {
release := st.Releases[i]
if !release.Desired() {
installed, err := isReleaseInstalled(helm, release)
installed, err := st.isReleaseInstalled(helm, release)
if err != nil {
return nil, err
} else if installed {
@ -330,7 +330,7 @@ func (st *HelmState) SyncReleases(helm helmexec.Interface, additionalValues []st
chart := normalizeChart(st.basePath, release.Chart)
var relErr *ReleaseError
if !release.Desired() {
installed, err := isReleaseInstalled(helm, *release)
installed, err := st.isReleaseInstalled(helm, *release)
if err != nil {
relErr = &ReleaseError{release, err}
} else if installed {
@ -735,7 +735,11 @@ func (st *HelmState) ReleaseStatuses(helm helmexec.Interface, workerLimit int) [
if !release.Desired() {
return nil
}
return helm.ReleaseStatus(release.Name)
flags := []string{}
flags = st.appendTillerFlags(flags, &release)
return helm.ReleaseStatus(release.Name, flags...)
})
}
@ -750,7 +754,9 @@ func (st *HelmState) DeleteReleases(helm helmexec.Interface, purge bool) []error
if purge {
flags = append(flags, "--purge")
}
installed, err := isReleaseInstalled(helm, release)
flags = st.appendTillerFlags(flags, &release)
installed, err := st.isReleaseInstalled(helm, release)
if err != nil {
return err
}
@ -773,6 +779,8 @@ func (st *HelmState) TestReleases(helm helmexec.Interface, cleanup bool, timeout
flags = append(flags, "--cleanup")
}
flags = append(flags, "--timeout", strconv.Itoa(timeout))
flags = st.appendTillerFlags(flags, &release)
return helm.TestRelease(release.Name, flags...)
})
}
@ -976,6 +984,15 @@ func findChartDirectory(topLevelDir string) (string, error) {
}
func (st *HelmState) appendTillerFlags(flags []string, release *ReleaseSpec) []string {
adds := st.tillerFlags(release)
for _, a := range adds {
flags = append(flags, a)
}
return flags
}
func (st *HelmState) tillerFlags(release *ReleaseSpec) []string {
flags := []string{}
if release.TillerNamespace != "" {
flags = append(flags, "--tiller-namespace", release.TillerNamespace)
} else if st.HelmDefaults.TillerNamespace != "" {

View File

@ -631,12 +631,18 @@ func Test_normalizeChart(t *testing.T) {
// mocking helmexec.Interface
type listKey struct {
filter string
flags string
}
type mockHelmExec struct {
charts []string
repo []string
releases []mockRelease
deleted []mockRelease
lists map[string]string
lists map[listKey]string
diffed []mockRelease
}
type mockRelease struct {
@ -682,21 +688,22 @@ func (helm *mockHelmExec) SyncRelease(name, chart string, flags ...string) error
return nil
}
func (helm *mockHelmExec) DiffRelease(name, chart string, flags ...string) error {
helm.diffed = append(helm.diffed, mockRelease{name: name, flags: flags})
return nil
}
func (helm *mockHelmExec) ReleaseStatus(release string) error {
func (helm *mockHelmExec) ReleaseStatus(release string, flags ...string) error {
if strings.Contains(release, "error") {
return errors.New("error")
}
helm.releases = append(helm.releases, mockRelease{name: release, flags: []string{}})
helm.releases = append(helm.releases, mockRelease{name: release, flags: flags})
return nil
}
func (helm *mockHelmExec) DeleteRelease(name string, flags ...string) error {
helm.deleted = append(helm.deleted, mockRelease{name: name, flags: flags})
return nil
}
func (helm *mockHelmExec) List(filter string) (string, error) {
return helm.lists[filter], nil
func (helm *mockHelmExec) List(filter string, flags ...string) (string, error) {
return helm.lists[listKey{filter: filter, flags: strings.Join(flags, "")}], nil
}
func (helm *mockHelmExec) DecryptSecret(name string) (string, error) {
return "", nil
@ -807,6 +814,18 @@ func TestHelmState_SyncReleases(t *testing.T) {
helm: &mockHelmExec{},
wantReleases: []mockRelease{{"releaseName", []string{}}},
},
{
name: "with tiller args",
releases: []ReleaseSpec{
{
Name: "releaseName",
Chart: "foo",
TillerNamespace: "tillerns",
},
},
helm: &mockHelmExec{},
wantReleases: []mockRelease{{"releaseName", []string{"--tiller-namespace", "tillerns"}}},
},
{
name: "escaped values",
releases: []ReleaseSpec{
@ -887,6 +906,120 @@ func TestHelmState_SyncReleases(t *testing.T) {
}
}
func TestHelmState_DiffReleases(t *testing.T) {
tests := []struct {
name string
releases []ReleaseSpec
helm *mockHelmExec
wantReleases []mockRelease
}{
{
name: "normal release",
releases: []ReleaseSpec{
{
Name: "releaseName",
Chart: "foo",
},
},
helm: &mockHelmExec{},
wantReleases: []mockRelease{{"releaseName", []string{}}},
},
{
name: "with tiller args",
releases: []ReleaseSpec{
{
Name: "releaseName",
Chart: "foo",
TillerNamespace: "tillerns",
},
},
helm: &mockHelmExec{},
wantReleases: []mockRelease{{"releaseName", []string{"--tiller-namespace", "tillerns"}}},
},
{
name: "escaped values",
releases: []ReleaseSpec{
{
Name: "releaseName",
Chart: "foo",
SetValues: []SetValue{
{
Name: "someList",
Value: "a,b,c",
},
{
Name: "json",
Value: "{\"name\": \"john\"}",
},
},
},
},
helm: &mockHelmExec{},
wantReleases: []mockRelease{{"releaseName", []string{"--set", "someList=a\\,b\\,c", "--set", "json=\\{\"name\": \"john\"\\}"}}},
},
{
name: "set single value from file",
releases: []ReleaseSpec{
{
Name: "releaseName",
Chart: "foo",
SetValues: []SetValue{
{
Name: "foo",
Value: "FOO",
},
{
Name: "bar",
File: "path/to/bar",
},
{
Name: "baz",
Value: "BAZ",
},
},
},
},
helm: &mockHelmExec{},
wantReleases: []mockRelease{{"releaseName", []string{"--set", "foo=FOO", "--set-file", "bar=path/to/bar", "--set", "baz=BAZ"}}},
},
{
name: "set single array value in an array",
releases: []ReleaseSpec{
{
Name: "releaseName",
Chart: "foo",
SetValues: []SetValue{
{
Name: "foo.bar[0]",
Values: []string{
"A",
"B",
},
},
},
},
},
helm: &mockHelmExec{},
wantReleases: []mockRelease{{"releaseName", []string{"--set", "foo.bar[0]={A,B}"}}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
state := &HelmState{
Releases: tt.releases,
logger: logger,
}
_, err := state.DiffReleases(tt.helm, []string{}, 1, false, false, false)
if err != nil {
fmt.Errorf("unexpected error: %v", err)
}
if !reflect.DeepEqual(tt.helm.diffed, tt.wantReleases) {
t.Errorf("HelmState.DiffReleases() for [%s] = %v, want %v", tt.name, tt.helm.releases, tt.wantReleases)
}
})
}
}
func TestHelmState_SyncReleasesCleanup(t *testing.T) {
tests := []struct {
name string
@ -1156,6 +1289,17 @@ func TestHelmState_ReleaseStatuses(t *testing.T) {
helm: &mockHelmExec{},
wantErr: false,
},
{
name: "with tiller args",
releases: []ReleaseSpec{
{
Name: "releaseA",
TillerNamespace: "tillerns",
},
},
helm: &mockHelmExec{},
want: []mockRelease{{"releaseA", []string{"--tiller-namespace", "tillerns"}}},
},
}
for _, tt := range tests {
i := func(t *testing.T) {
@ -1190,12 +1334,13 @@ func TestHelmState_ReleaseStatuses(t *testing.T) {
func TestHelmState_TestReleasesNoCleanUp(t *testing.T) {
tests := []struct {
name string
cleanup bool
releases []ReleaseSpec
helm *mockHelmExec
want []mockRelease
wantErr bool
name string
cleanup bool
releases []ReleaseSpec
helm *mockHelmExec
want []mockRelease
wantErr bool
tillerNamespace string
}{
{
name: "happy path",
@ -1228,6 +1373,17 @@ func TestHelmState_TestReleasesNoCleanUp(t *testing.T) {
helm: &mockHelmExec{},
wantErr: true,
},
{
name: "with tiller args",
releases: []ReleaseSpec{
{
Name: "releaseA",
TillerNamespace: "tillerns",
},
},
helm: &mockHelmExec{},
want: []mockRelease{{"releaseA", []string{"--timeout", "1", "--tiller-namespace", "tillerns"}}},
},
}
for _, tt := range tests {
i := func(t *testing.T) {
@ -1297,12 +1453,14 @@ func TestHelmState_NoReleaseMatched(t *testing.T) {
func TestHelmState_Delete(t *testing.T) {
tests := []struct {
name string
deleted []mockRelease
wantErr bool
desired *bool
installed bool
purge bool
name string
deleted []mockRelease
wantErr bool
desired *bool
installed bool
purge bool
flags string
tillerNamespace string
}{
{
name: "desired and installed (purge=false)",
@ -1376,12 +1534,23 @@ func TestHelmState_Delete(t *testing.T) {
purge: true,
deleted: []mockRelease{},
},
{
name: "with tiller args",
wantErr: false,
desired: nil,
installed: true,
purge: true,
tillerNamespace: "tillerns",
flags: "--tiller-namespacetillerns",
deleted: []mockRelease{{"releaseA", []string{"--purge", "--tiller-namespace", "tillerns"}}},
},
}
for _, tt := range tests {
i := func(t *testing.T) {
release := ReleaseSpec{
Name: "releaseA",
Installed: tt.desired,
Name: "releaseA",
Installed: tt.desired,
TillerNamespace: tt.tillerNamespace,
}
releases := []ReleaseSpec{
release,
@ -1391,15 +1560,15 @@ func TestHelmState_Delete(t *testing.T) {
logger: logger,
}
helm := &mockHelmExec{
lists: map[string]string{},
lists: map[listKey]string{},
deleted: []mockRelease{},
}
if tt.installed {
helm.lists["^releaseA$"] = "releaseA"
helm.lists[listKey{filter: "^releaseA$", flags: tt.flags}] = "releaseA"
}
errs := state.DeleteReleases(helm, tt.purge)
if (errs != nil) != tt.wantErr {
t.Errorf("DeleteREleases() for %s error = %v, wantErr %v", tt.name, errs, tt.wantErr)
t.Errorf("DeleteReleases() for %s error = %v, wantErr %v", tt.name, errs, tt.wantErr)
return
}
if !reflect.DeepEqual(tt.deleted, helm.deleted) {