diff --git a/pkg/tiller/release_list_test.go b/pkg/tiller/release_list_test.go new file mode 100644 index 000000000..64877422a --- /dev/null +++ b/pkg/tiller/release_list_test.go @@ -0,0 +1,233 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tiller + +import ( + "fmt" + "testing" + + "k8s.io/helm/pkg/proto/hapi/release" + "k8s.io/helm/pkg/proto/hapi/services" +) + +func TestListReleases(t *testing.T) { + rs := rsFixture() + num := 7 + for i := 0; i < num; i++ { + rel := releaseStub() + rel.Name = fmt.Sprintf("rel-%d", i) + if err := rs.env.Releases.Create(rel); err != nil { + t.Fatalf("Could not store mock release: %s", err) + } + } + + mrs := &mockListServer{} + if err := rs.ListReleases(&services.ListReleasesRequest{Offset: "", Limit: 64}, mrs); err != nil { + t.Fatalf("Failed listing: %s", err) + } + + if len(mrs.val.Releases) != num { + t.Errorf("Expected %d releases, got %d", num, len(mrs.val.Releases)) + } +} + +func TestListReleasesByStatus(t *testing.T) { + rs := rsFixture() + stubs := []*release.Release{ + namedReleaseStub("kamal", release.Status_DEPLOYED), + namedReleaseStub("astrolabe", release.Status_DELETED), + namedReleaseStub("octant", release.Status_FAILED), + namedReleaseStub("sextant", release.Status_UNKNOWN), + } + for _, stub := range stubs { + if err := rs.env.Releases.Create(stub); err != nil { + t.Fatalf("Could not create stub: %s", err) + } + } + + tests := []struct { + statusCodes []release.Status_Code + names []string + }{ + { + names: []string{"kamal"}, + statusCodes: []release.Status_Code{release.Status_DEPLOYED}, + }, + { + names: []string{"astrolabe"}, + statusCodes: []release.Status_Code{release.Status_DELETED}, + }, + { + names: []string{"kamal", "octant"}, + statusCodes: []release.Status_Code{release.Status_DEPLOYED, release.Status_FAILED}, + }, + { + names: []string{"kamal", "astrolabe", "octant", "sextant"}, + statusCodes: []release.Status_Code{ + release.Status_DEPLOYED, + release.Status_DELETED, + release.Status_FAILED, + release.Status_UNKNOWN, + }, + }, + } + + for i, tt := range tests { + mrs := &mockListServer{} + if err := rs.ListReleases(&services.ListReleasesRequest{StatusCodes: tt.statusCodes, Offset: "", Limit: 64}, mrs); err != nil { + t.Fatalf("Failed listing %d: %s", i, err) + } + + if len(tt.names) != len(mrs.val.Releases) { + t.Fatalf("Expected %d releases, got %d", len(tt.names), len(mrs.val.Releases)) + } + + for _, name := range tt.names { + found := false + for _, rel := range mrs.val.Releases { + if rel.Name == name { + found = true + } + } + if !found { + t.Errorf("%d: Did not find name %q", i, name) + } + } + } +} + +func TestListReleasesSort(t *testing.T) { + rs := rsFixture() + + // Put them in by reverse order so that the mock doesn't "accidentally" + // sort. + num := 7 + for i := num; i > 0; i-- { + rel := releaseStub() + rel.Name = fmt.Sprintf("rel-%d", i) + if err := rs.env.Releases.Create(rel); err != nil { + t.Fatalf("Could not store mock release: %s", err) + } + } + + limit := 6 + mrs := &mockListServer{} + req := &services.ListReleasesRequest{ + Offset: "", + Limit: int64(limit), + SortBy: services.ListSort_NAME, + } + if err := rs.ListReleases(req, mrs); err != nil { + t.Fatalf("Failed listing: %s", err) + } + + if len(mrs.val.Releases) != limit { + t.Errorf("Expected %d releases, got %d", limit, len(mrs.val.Releases)) + } + + for i := 0; i < limit; i++ { + n := fmt.Sprintf("rel-%d", i+1) + if mrs.val.Releases[i].Name != n { + t.Errorf("Expected %q, got %q", n, mrs.val.Releases[i].Name) + } + } +} + +func TestListReleasesFilter(t *testing.T) { + rs := rsFixture() + names := []string{ + "axon", + "dendrite", + "neuron", + "neuroglia", + "synapse", + "nucleus", + "organelles", + } + num := 7 + for i := 0; i < num; i++ { + rel := releaseStub() + rel.Name = names[i] + if err := rs.env.Releases.Create(rel); err != nil { + t.Fatalf("Could not store mock release: %s", err) + } + } + + mrs := &mockListServer{} + req := &services.ListReleasesRequest{ + Offset: "", + Limit: 64, + Filter: "neuro[a-z]+", + SortBy: services.ListSort_NAME, + } + if err := rs.ListReleases(req, mrs); err != nil { + t.Fatalf("Failed listing: %s", err) + } + + if len(mrs.val.Releases) != 2 { + t.Errorf("Expected 2 releases, got %d", len(mrs.val.Releases)) + } + + if mrs.val.Releases[0].Name != "neuroglia" { + t.Errorf("Unexpected sort order: %v.", mrs.val.Releases) + } + if mrs.val.Releases[1].Name != "neuron" { + t.Errorf("Unexpected sort order: %v.", mrs.val.Releases) + } +} + +func TestReleasesNamespace(t *testing.T) { + rs := rsFixture() + + names := []string{ + "axon", + "dendrite", + "neuron", + "ribosome", + } + + namespaces := []string{ + "default", + "test123", + "test123", + "cerebellum", + } + num := 4 + for i := 0; i < num; i++ { + rel := releaseStub() + rel.Name = names[i] + rel.Namespace = namespaces[i] + if err := rs.env.Releases.Create(rel); err != nil { + t.Fatalf("Could not store mock release: %s", err) + } + } + + mrs := &mockListServer{} + req := &services.ListReleasesRequest{ + Offset: "", + Limit: 64, + Namespace: "test123", + } + + if err := rs.ListReleases(req, mrs); err != nil { + t.Fatalf("Failed listing: %s", err) + } + + if len(mrs.val.Releases) != 2 { + t.Errorf("Expected 2 releases, got %d", len(mrs.val.Releases)) + } +} diff --git a/pkg/tiller/release_rollback_test.go b/pkg/tiller/release_rollback_test.go index 965788bdd..11ba9d7e2 100644 --- a/pkg/tiller/release_rollback_test.go +++ b/pkg/tiller/release_rollback_test.go @@ -191,3 +191,36 @@ func TestRollbackReleaseNoHooks(t *testing.T) { t.Errorf("Expected that no hooks were run. Got %d", hl) } } + +func TestRollbackReleaseFailure(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rs.env.Releases.Create(rel) + upgradedRel := upgradeReleaseVersion(rel) + rs.env.Releases.Update(rel) + rs.env.Releases.Create(upgradedRel) + + req := &services.RollbackReleaseRequest{ + Name: rel.Name, + DisableHooks: true, + } + + rs.env.KubeClient = newUpdateFailingKubeClient() + res, err := rs.RollbackRelease(c, req) + if err == nil { + t.Error("Expected failed rollback") + } + + if targetStatus := res.Release.Info.Status.Code; targetStatus != release.Status_FAILED { + t.Errorf("Expected FAILED release. Got %v", targetStatus) + } + + oldRelease, err := rs.env.Releases.Get(rel.Name, rel.Version) + if err != nil { + t.Errorf("Expected to be able to get previous release") + } + if oldStatus := oldRelease.Info.Status.Code; oldStatus != release.Status_SUPERSEDED { + t.Errorf("Expected SUPERSEDED status on previous Release version. Got %v", oldStatus) + } +} diff --git a/pkg/tiller/release_server_test.go b/pkg/tiller/release_server_test.go index ae5947e01..55fd10f9d 100644 --- a/pkg/tiller/release_server_test.go +++ b/pkg/tiller/release_server_test.go @@ -692,299 +692,6 @@ func TestInstallRelease_ReuseName(t *testing.T) { } } -func TestUpdateRelease(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - rs.env.Releases.Create(rel) - - req := &services.UpdateReleaseRequest{ - Name: rel.Name, - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, - }, - }, - } - res, err := rs.UpdateRelease(c, req) - if err != nil { - t.Fatalf("Failed updated: %s", err) - } - - if res.Release.Name == "" { - t.Errorf("Expected release name.") - } - - if res.Release.Name != rel.Name { - t.Errorf("Updated release name does not match previous release name. Expected %s, got %s", rel.Name, res.Release.Name) - } - - if res.Release.Namespace != rel.Namespace { - t.Errorf("Expected release namespace '%s', got '%s'.", rel.Namespace, res.Release.Namespace) - } - - updated, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) - if err != nil { - t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) - } - - if len(updated.Hooks) != 1 { - t.Fatalf("Expected 1 hook, got %d", len(updated.Hooks)) - } - if updated.Hooks[0].Manifest != manifestWithUpgradeHooks { - t.Errorf("Unexpected manifest: %v", updated.Hooks[0].Manifest) - } - - if updated.Hooks[0].Events[0] != release.Hook_POST_UPGRADE { - t.Errorf("Expected event 0 to be post upgrade") - } - - if updated.Hooks[0].Events[1] != release.Hook_PRE_UPGRADE { - t.Errorf("Expected event 0 to be pre upgrade") - } - - if len(res.Release.Manifest) == 0 { - t.Errorf("No manifest returned: %v", res.Release) - } - - if res.Release.Config == nil { - t.Errorf("Got release without config: %#v", res.Release) - } else if res.Release.Config.Raw != rel.Config.Raw { - t.Errorf("Expected release values %q, got %q", rel.Config.Raw, res.Release.Config.Raw) - } - - if len(updated.Manifest) == 0 { - t.Errorf("Expected manifest in %v", res) - } - - if !strings.Contains(updated.Manifest, "---\n# Source: hello/templates/hello\nhello: world") { - t.Errorf("unexpected output: %s", rel.Manifest) - } - - if res.Release.Version != 2 { - t.Errorf("Expected release version to be %v, got %v", 2, res.Release.Version) - } - - edesc := "Upgrade complete" - if got := res.Release.Info.Description; got != edesc { - t.Errorf("Expected description %q, got %q", edesc, got) - } -} -func TestUpdateRelease_ResetValues(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - rs.env.Releases.Create(rel) - - req := &services.UpdateReleaseRequest{ - Name: rel.Name, - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, - }, - }, - ResetValues: true, - } - res, err := rs.UpdateRelease(c, req) - if err != nil { - t.Fatalf("Failed updated: %s", err) - } - // This should have been unset. Config: &chart.Config{Raw: `name: value`}, - if res.Release.Config != nil && res.Release.Config.Raw != "" { - t.Errorf("Expected chart config to be empty, got %q", res.Release.Config.Raw) - } -} - -func TestUpdateRelease_ReuseValues(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - rs.env.Releases.Create(rel) - - req := &services.UpdateReleaseRequest{ - Name: rel.Name, - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, - }, - // Since reuseValues is set, this should get ignored. - Values: &chart.Config{Raw: "foo: bar\n"}, - }, - Values: &chart.Config{Raw: "name2: val2"}, - ReuseValues: true, - } - res, err := rs.UpdateRelease(c, req) - if err != nil { - t.Fatalf("Failed updated: %s", err) - } - // This should have been overwritten with the old value. - expect := "name: value\n" - if res.Release.Chart.Values != nil && res.Release.Chart.Values.Raw != expect { - t.Errorf("Expected chart values to be %q, got %q", expect, res.Release.Chart.Values.Raw) - } - // This should have the newly-passed overrides. - expect = "name2: val2" - if res.Release.Config != nil && res.Release.Config.Raw != expect { - t.Errorf("Expected request config to be %q, got %q", expect, res.Release.Config.Raw) - } -} - -func TestUpdateRelease_ResetReuseValues(t *testing.T) { - // This verifies that when both reset and reuse are set, reset wins. - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - rs.env.Releases.Create(rel) - - req := &services.UpdateReleaseRequest{ - Name: rel.Name, - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, - }, - }, - ResetValues: true, - ReuseValues: true, - } - res, err := rs.UpdateRelease(c, req) - if err != nil { - t.Fatalf("Failed updated: %s", err) - } - // This should have been unset. Config: &chart.Config{Raw: `name: value`}, - if res.Release.Config != nil && res.Release.Config.Raw != "" { - t.Errorf("Expected chart config to be empty, got %q", res.Release.Config.Raw) - } -} - -func TestUpdateReleaseFailure(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - rs.env.Releases.Create(rel) - rs.env.KubeClient = newUpdateFailingKubeClient() - - req := &services.UpdateReleaseRequest{ - Name: rel.Name, - DisableHooks: true, - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello"}, - Templates: []*chart.Template{ - {Name: "templates/something", Data: []byte("hello: world")}, - }, - }, - } - - res, err := rs.UpdateRelease(c, req) - if err == nil { - t.Error("Expected failed update") - } - - if updatedStatus := res.Release.Info.Status.Code; updatedStatus != release.Status_FAILED { - t.Errorf("Expected FAILED release. Got %d", updatedStatus) - } - - edesc := "Upgrade \"angry-panda\" failed: Failed update in kube client" - if got := res.Release.Info.Description; got != edesc { - t.Errorf("Expected description %q, got %q", edesc, got) - } - - oldRelease, err := rs.env.Releases.Get(rel.Name, rel.Version) - if err != nil { - t.Errorf("Expected to be able to get previous release") - } - if oldStatus := oldRelease.Info.Status.Code; oldStatus != release.Status_SUPERSEDED { - t.Errorf("Expected SUPERSEDED status on previous Release version. Got %v", oldStatus) - } -} - -func TestRollbackReleaseFailure(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - rs.env.Releases.Create(rel) - upgradedRel := upgradeReleaseVersion(rel) - rs.env.Releases.Update(rel) - rs.env.Releases.Create(upgradedRel) - - req := &services.RollbackReleaseRequest{ - Name: rel.Name, - DisableHooks: true, - } - - rs.env.KubeClient = newUpdateFailingKubeClient() - res, err := rs.RollbackRelease(c, req) - if err == nil { - t.Error("Expected failed rollback") - } - - if targetStatus := res.Release.Info.Status.Code; targetStatus != release.Status_FAILED { - t.Errorf("Expected FAILED release. Got %v", targetStatus) - } - - oldRelease, err := rs.env.Releases.Get(rel.Name, rel.Version) - if err != nil { - t.Errorf("Expected to be able to get previous release") - } - if oldStatus := oldRelease.Info.Status.Code; oldStatus != release.Status_SUPERSEDED { - t.Errorf("Expected SUPERSEDED status on previous Release version. Got %v", oldStatus) - } -} - -func TestUpdateReleaseNoHooks(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - rs.env.Releases.Create(rel) - - req := &services.UpdateReleaseRequest{ - Name: rel.Name, - DisableHooks: true, - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, - }, - }, - } - - res, err := rs.UpdateRelease(c, req) - if err != nil { - t.Fatalf("Failed updated: %s", err) - } - - if hl := res.Release.Hooks[0].LastRun; hl != nil { - t.Errorf("Expected that no hooks were run. Got %d", hl) - } - -} - -func TestUpdateReleaseNoChanges(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - rs.env.Releases.Create(rel) - - req := &services.UpdateReleaseRequest{ - Name: rel.Name, - DisableHooks: true, - Chart: rel.GetChart(), - } - - _, err := rs.UpdateRelease(c, req) - if err != nil { - t.Fatalf("Failed updated: %s", err) - } -} - func releaseWithKeepStub(rlsName string) *release.Release { ch := &chart.Chart{ Metadata: &chart.Metadata{ @@ -1010,214 +717,6 @@ func releaseWithKeepStub(rlsName string) *release.Release { } } -func TestListReleases(t *testing.T) { - rs := rsFixture() - num := 7 - for i := 0; i < num; i++ { - rel := releaseStub() - rel.Name = fmt.Sprintf("rel-%d", i) - if err := rs.env.Releases.Create(rel); err != nil { - t.Fatalf("Could not store mock release: %s", err) - } - } - - mrs := &mockListServer{} - if err := rs.ListReleases(&services.ListReleasesRequest{Offset: "", Limit: 64}, mrs); err != nil { - t.Fatalf("Failed listing: %s", err) - } - - if len(mrs.val.Releases) != num { - t.Errorf("Expected %d releases, got %d", num, len(mrs.val.Releases)) - } -} - -func TestListReleasesByStatus(t *testing.T) { - rs := rsFixture() - stubs := []*release.Release{ - namedReleaseStub("kamal", release.Status_DEPLOYED), - namedReleaseStub("astrolabe", release.Status_DELETED), - namedReleaseStub("octant", release.Status_FAILED), - namedReleaseStub("sextant", release.Status_UNKNOWN), - } - for _, stub := range stubs { - if err := rs.env.Releases.Create(stub); err != nil { - t.Fatalf("Could not create stub: %s", err) - } - } - - tests := []struct { - statusCodes []release.Status_Code - names []string - }{ - { - names: []string{"kamal"}, - statusCodes: []release.Status_Code{release.Status_DEPLOYED}, - }, - { - names: []string{"astrolabe"}, - statusCodes: []release.Status_Code{release.Status_DELETED}, - }, - { - names: []string{"kamal", "octant"}, - statusCodes: []release.Status_Code{release.Status_DEPLOYED, release.Status_FAILED}, - }, - { - names: []string{"kamal", "astrolabe", "octant", "sextant"}, - statusCodes: []release.Status_Code{ - release.Status_DEPLOYED, - release.Status_DELETED, - release.Status_FAILED, - release.Status_UNKNOWN, - }, - }, - } - - for i, tt := range tests { - mrs := &mockListServer{} - if err := rs.ListReleases(&services.ListReleasesRequest{StatusCodes: tt.statusCodes, Offset: "", Limit: 64}, mrs); err != nil { - t.Fatalf("Failed listing %d: %s", i, err) - } - - if len(tt.names) != len(mrs.val.Releases) { - t.Fatalf("Expected %d releases, got %d", len(tt.names), len(mrs.val.Releases)) - } - - for _, name := range tt.names { - found := false - for _, rel := range mrs.val.Releases { - if rel.Name == name { - found = true - } - } - if !found { - t.Errorf("%d: Did not find name %q", i, name) - } - } - } -} - -func TestListReleasesSort(t *testing.T) { - rs := rsFixture() - - // Put them in by reverse order so that the mock doesn't "accidentally" - // sort. - num := 7 - for i := num; i > 0; i-- { - rel := releaseStub() - rel.Name = fmt.Sprintf("rel-%d", i) - if err := rs.env.Releases.Create(rel); err != nil { - t.Fatalf("Could not store mock release: %s", err) - } - } - - limit := 6 - mrs := &mockListServer{} - req := &services.ListReleasesRequest{ - Offset: "", - Limit: int64(limit), - SortBy: services.ListSort_NAME, - } - if err := rs.ListReleases(req, mrs); err != nil { - t.Fatalf("Failed listing: %s", err) - } - - if len(mrs.val.Releases) != limit { - t.Errorf("Expected %d releases, got %d", limit, len(mrs.val.Releases)) - } - - for i := 0; i < limit; i++ { - n := fmt.Sprintf("rel-%d", i+1) - if mrs.val.Releases[i].Name != n { - t.Errorf("Expected %q, got %q", n, mrs.val.Releases[i].Name) - } - } -} - -func TestListReleasesFilter(t *testing.T) { - rs := rsFixture() - names := []string{ - "axon", - "dendrite", - "neuron", - "neuroglia", - "synapse", - "nucleus", - "organelles", - } - num := 7 - for i := 0; i < num; i++ { - rel := releaseStub() - rel.Name = names[i] - if err := rs.env.Releases.Create(rel); err != nil { - t.Fatalf("Could not store mock release: %s", err) - } - } - - mrs := &mockListServer{} - req := &services.ListReleasesRequest{ - Offset: "", - Limit: 64, - Filter: "neuro[a-z]+", - SortBy: services.ListSort_NAME, - } - if err := rs.ListReleases(req, mrs); err != nil { - t.Fatalf("Failed listing: %s", err) - } - - if len(mrs.val.Releases) != 2 { - t.Errorf("Expected 2 releases, got %d", len(mrs.val.Releases)) - } - - if mrs.val.Releases[0].Name != "neuroglia" { - t.Errorf("Unexpected sort order: %v.", mrs.val.Releases) - } - if mrs.val.Releases[1].Name != "neuron" { - t.Errorf("Unexpected sort order: %v.", mrs.val.Releases) - } -} - -func TestReleasesNamespace(t *testing.T) { - rs := rsFixture() - - names := []string{ - "axon", - "dendrite", - "neuron", - "ribosome", - } - - namespaces := []string{ - "default", - "test123", - "test123", - "cerebellum", - } - num := 4 - for i := 0; i < num; i++ { - rel := releaseStub() - rel.Name = names[i] - rel.Namespace = namespaces[i] - if err := rs.env.Releases.Create(rel); err != nil { - t.Fatalf("Could not store mock release: %s", err) - } - } - - mrs := &mockListServer{} - req := &services.ListReleasesRequest{ - Offset: "", - Limit: 64, - Namespace: "test123", - } - - if err := rs.ListReleases(req, mrs); err != nil { - t.Fatalf("Failed listing: %s", err) - } - - if len(mrs.val.Releases) != 2 { - t.Errorf("Expected 2 releases, got %d", len(mrs.val.Releases)) - } -} - func MockEnvironment() *environment.Environment { e := environment.New() e.Releases = storage.Init(driver.NewMemory()) diff --git a/pkg/tiller/release_update_test.go b/pkg/tiller/release_update_test.go new file mode 100644 index 000000000..cba07e11e --- /dev/null +++ b/pkg/tiller/release_update_test.go @@ -0,0 +1,286 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tiller + +import ( + "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/proto/hapi/chart" + "k8s.io/helm/pkg/proto/hapi/release" + "k8s.io/helm/pkg/proto/hapi/services" + "strings" + "testing" +) + +func TestUpdateRelease(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rs.env.Releases.Create(rel) + + req := &services.UpdateReleaseRequest{ + Name: rel.Name, + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, + }, + }, + } + res, err := rs.UpdateRelease(c, req) + if err != nil { + t.Fatalf("Failed updated: %s", err) + } + + if res.Release.Name == "" { + t.Errorf("Expected release name.") + } + + if res.Release.Name != rel.Name { + t.Errorf("Updated release name does not match previous release name. Expected %s, got %s", rel.Name, res.Release.Name) + } + + if res.Release.Namespace != rel.Namespace { + t.Errorf("Expected release namespace '%s', got '%s'.", rel.Namespace, res.Release.Namespace) + } + + updated, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) + if err != nil { + t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) + } + + if len(updated.Hooks) != 1 { + t.Fatalf("Expected 1 hook, got %d", len(updated.Hooks)) + } + if updated.Hooks[0].Manifest != manifestWithUpgradeHooks { + t.Errorf("Unexpected manifest: %v", updated.Hooks[0].Manifest) + } + + if updated.Hooks[0].Events[0] != release.Hook_POST_UPGRADE { + t.Errorf("Expected event 0 to be post upgrade") + } + + if updated.Hooks[0].Events[1] != release.Hook_PRE_UPGRADE { + t.Errorf("Expected event 0 to be pre upgrade") + } + + if len(res.Release.Manifest) == 0 { + t.Errorf("No manifest returned: %v", res.Release) + } + + if res.Release.Config == nil { + t.Errorf("Got release without config: %#v", res.Release) + } else if res.Release.Config.Raw != rel.Config.Raw { + t.Errorf("Expected release values %q, got %q", rel.Config.Raw, res.Release.Config.Raw) + } + + if len(updated.Manifest) == 0 { + t.Errorf("Expected manifest in %v", res) + } + + if !strings.Contains(updated.Manifest, "---\n# Source: hello/templates/hello\nhello: world") { + t.Errorf("unexpected output: %s", rel.Manifest) + } + + if res.Release.Version != 2 { + t.Errorf("Expected release version to be %v, got %v", 2, res.Release.Version) + } + + edesc := "Upgrade complete" + if got := res.Release.Info.Description; got != edesc { + t.Errorf("Expected description %q, got %q", edesc, got) + } +} +func TestUpdateRelease_ResetValues(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rs.env.Releases.Create(rel) + + req := &services.UpdateReleaseRequest{ + Name: rel.Name, + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, + }, + }, + ResetValues: true, + } + res, err := rs.UpdateRelease(c, req) + if err != nil { + t.Fatalf("Failed updated: %s", err) + } + // This should have been unset. Config: &chart.Config{Raw: `name: value`}, + if res.Release.Config != nil && res.Release.Config.Raw != "" { + t.Errorf("Expected chart config to be empty, got %q", res.Release.Config.Raw) + } +} + +func TestUpdateRelease_ReuseValues(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rs.env.Releases.Create(rel) + + req := &services.UpdateReleaseRequest{ + Name: rel.Name, + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, + }, + // Since reuseValues is set, this should get ignored. + Values: &chart.Config{Raw: "foo: bar\n"}, + }, + Values: &chart.Config{Raw: "name2: val2"}, + ReuseValues: true, + } + res, err := rs.UpdateRelease(c, req) + if err != nil { + t.Fatalf("Failed updated: %s", err) + } + // This should have been overwritten with the old value. + expect := "name: value\n" + if res.Release.Chart.Values != nil && res.Release.Chart.Values.Raw != expect { + t.Errorf("Expected chart values to be %q, got %q", expect, res.Release.Chart.Values.Raw) + } + // This should have the newly-passed overrides. + expect = "name2: val2" + if res.Release.Config != nil && res.Release.Config.Raw != expect { + t.Errorf("Expected request config to be %q, got %q", expect, res.Release.Config.Raw) + } +} + +func TestUpdateRelease_ResetReuseValues(t *testing.T) { + // This verifies that when both reset and reuse are set, reset wins. + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rs.env.Releases.Create(rel) + + req := &services.UpdateReleaseRequest{ + Name: rel.Name, + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, + }, + }, + ResetValues: true, + ReuseValues: true, + } + res, err := rs.UpdateRelease(c, req) + if err != nil { + t.Fatalf("Failed updated: %s", err) + } + // This should have been unset. Config: &chart.Config{Raw: `name: value`}, + if res.Release.Config != nil && res.Release.Config.Raw != "" { + t.Errorf("Expected chart config to be empty, got %q", res.Release.Config.Raw) + } +} + +func TestUpdateReleaseFailure(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rs.env.Releases.Create(rel) + rs.env.KubeClient = newUpdateFailingKubeClient() + + req := &services.UpdateReleaseRequest{ + Name: rel.Name, + DisableHooks: true, + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "templates/something", Data: []byte("hello: world")}, + }, + }, + } + + res, err := rs.UpdateRelease(c, req) + if err == nil { + t.Error("Expected failed update") + } + + if updatedStatus := res.Release.Info.Status.Code; updatedStatus != release.Status_FAILED { + t.Errorf("Expected FAILED release. Got %d", updatedStatus) + } + + edesc := "Upgrade \"angry-panda\" failed: Failed update in kube client" + if got := res.Release.Info.Description; got != edesc { + t.Errorf("Expected description %q, got %q", edesc, got) + } + + oldRelease, err := rs.env.Releases.Get(rel.Name, rel.Version) + if err != nil { + t.Errorf("Expected to be able to get previous release") + } + if oldStatus := oldRelease.Info.Status.Code; oldStatus != release.Status_SUPERSEDED { + t.Errorf("Expected SUPERSEDED status on previous Release version. Got %v", oldStatus) + } +} + +func TestUpdateReleaseNoHooks(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rs.env.Releases.Create(rel) + + req := &services.UpdateReleaseRequest{ + Name: rel.Name, + DisableHooks: true, + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, + }, + }, + } + + res, err := rs.UpdateRelease(c, req) + if err != nil { + t.Fatalf("Failed updated: %s", err) + } + + if hl := res.Release.Hooks[0].LastRun; hl != nil { + t.Errorf("Expected that no hooks were run. Got %d", hl) + } + +} + +func TestUpdateReleaseNoChanges(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rs.env.Releases.Create(rel) + + req := &services.UpdateReleaseRequest{ + Name: rel.Name, + DisableHooks: true, + Chart: rel.GetChart(), + } + + _, err := rs.UpdateRelease(c, req) + if err != nil { + t.Fatalf("Failed updated: %s", err) + } +}