diff --git a/cmd/helm/delete_test.go b/cmd/helm/delete_test.go index 4f1bd29a6..829d17906 100644 --- a/cmd/helm/delete_test.go +++ b/cmd/helm/delete_test.go @@ -23,6 +23,7 @@ import ( "github.com/spf13/cobra" "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/proto/hapi/release" ) func TestDelete(t *testing.T) { @@ -33,28 +34,32 @@ func TestDelete(t *testing.T) { args: []string{"aeneas"}, flags: []string{}, expected: "", // Output of a delete is an empty string and exit 0. - resp: releaseMock(&releaseOptions{name: "aeneas"}), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"}), + rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"})}, }, { name: "delete with timeout", args: []string{"aeneas"}, flags: []string{"--timeout", "120"}, expected: "", - resp: releaseMock(&releaseOptions{name: "aeneas"}), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"}), + rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"})}, }, { name: "delete without hooks", args: []string{"aeneas"}, flags: []string{"--no-hooks"}, expected: "", - resp: releaseMock(&releaseOptions{name: "aeneas"}), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"}), + rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"})}, }, { name: "purge", args: []string{"aeneas"}, flags: []string{"--purge"}, expected: "", - resp: releaseMock(&releaseOptions{name: "aeneas"}), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"}), + rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"})}, }, { name: "delete without release", diff --git a/cmd/helm/get_hooks_test.go b/cmd/helm/get_hooks_test.go index 3e6132fbf..e578c2533 100644 --- a/cmd/helm/get_hooks_test.go +++ b/cmd/helm/get_hooks_test.go @@ -23,6 +23,7 @@ import ( "github.com/spf13/cobra" "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/proto/hapi/release" ) func TestGetHooks(t *testing.T) { @@ -30,8 +31,9 @@ func TestGetHooks(t *testing.T) { { name: "get hooks with release", args: []string{"aeneas"}, - expected: mockHookTemplate, - resp: releaseMock(&releaseOptions{name: "aeneas"}), + expected: helm.MockHookTemplate, + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"}), + rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"})}, }, { name: "get hooks without args", diff --git a/cmd/helm/get_manifest_test.go b/cmd/helm/get_manifest_test.go index 4ba80e2ad..286b5628a 100644 --- a/cmd/helm/get_manifest_test.go +++ b/cmd/helm/get_manifest_test.go @@ -23,6 +23,7 @@ import ( "github.com/spf13/cobra" "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/proto/hapi/release" ) func TestGetManifest(t *testing.T) { @@ -30,8 +31,9 @@ func TestGetManifest(t *testing.T) { { name: "get manifest with release", args: []string{"juno"}, - expected: mockManifest, - resp: releaseMock(&releaseOptions{name: "juno"}), + expected: helm.MockManifest, + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "juno"}), + rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "juno"})}, }, { name: "get manifest without args", diff --git a/cmd/helm/get_test.go b/cmd/helm/get_test.go index 23b82c04d..a6e72987a 100644 --- a/cmd/helm/get_test.go +++ b/cmd/helm/get_test.go @@ -23,15 +23,17 @@ import ( "github.com/spf13/cobra" "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/proto/hapi/release" ) func TestGetCmd(t *testing.T) { tests := []releaseCase{ { name: "get with a release", - resp: releaseMock(&releaseOptions{name: "thomas-guide"}), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide"}), args: []string{"thomas-guide"}, - expected: "REVISION: 1\nRELEASED: (.*)\nCHART: foo-0.1.0-beta.1\nUSER-SUPPLIED VALUES:\nname: \"value\"\nCOMPUTED VALUES:\nname: value\n\nHOOKS:\n---\n# pre-install-hook\n" + mockHookTemplate + "\nMANIFEST:", + expected: "REVISION: 1\nRELEASED: (.*)\nCHART: foo-0.1.0-beta.1\nUSER-SUPPLIED VALUES:\nname: \"value\"\nCOMPUTED VALUES:\nname: value\n\nHOOKS:\n---\n# pre-install-hook\n" + helm.MockHookTemplate + "\nMANIFEST:", + rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide"})}, }, { name: "get requires release name arg", diff --git a/cmd/helm/get_values_test.go b/cmd/helm/get_values_test.go index 4032253fe..30b2ba4b8 100644 --- a/cmd/helm/get_values_test.go +++ b/cmd/helm/get_values_test.go @@ -23,15 +23,17 @@ import ( "github.com/spf13/cobra" "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/proto/hapi/release" ) func TestGetValuesCmd(t *testing.T) { tests := []releaseCase{ { name: "get values with a release", - resp: releaseMock(&releaseOptions{name: "thomas-guide"}), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide"}), args: []string{"thomas-guide"}, expected: "name: \"value\"", + rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide"})}, }, { name: "get values requires release name arg", diff --git a/cmd/helm/helm_test.go b/cmd/helm/helm_test.go index 2a2af1359..7d74d66b7 100644 --- a/cmd/helm/helm_test.go +++ b/cmd/helm/helm_test.go @@ -21,115 +21,30 @@ import ( "fmt" "io" "io/ioutil" - "math/rand" "os" "path/filepath" "regexp" "strings" "testing" - "github.com/golang/protobuf/ptypes/timestamp" "github.com/spf13/cobra" "k8s.io/helm/pkg/helm" "k8s.io/helm/pkg/helm/helmpath" - "k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/proto/hapi/release" "k8s.io/helm/pkg/repo" ) -var mockHookTemplate = `apiVersion: v1 -kind: Job -metadata: - annotations: - "helm.sh/hooks": pre-install -` - -var mockManifest = `apiVersion: v1 -kind: Secret -metadata: - name: fixture -` - -type releaseOptions struct { - name string - version int32 - chart *chart.Chart - statusCode release.Status_Code - namespace string -} - -func releaseMock(opts *releaseOptions) *release.Release { - date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0} - - name := opts.name - if name == "" { - name = "testrelease-" + string(rand.Intn(100)) - } - - var version int32 = 1 - if opts.version != 0 { - version = opts.version - } - - namespace := opts.namespace - if namespace == "" { - namespace = "default" - } - - ch := opts.chart - if opts.chart == nil { - ch = &chart.Chart{ - Metadata: &chart.Metadata{ - Name: "foo", - Version: "0.1.0-beta.1", - }, - Templates: []*chart.Template{ - {Name: "templates/foo.tpl", Data: []byte(mockManifest)}, - }, - } - } - - scode := release.Status_DEPLOYED - if opts.statusCode > 0 { - scode = opts.statusCode - } - - return &release.Release{ - Name: name, - Info: &release.Info{ - FirstDeployed: &date, - LastDeployed: &date, - Status: &release.Status{Code: scode}, - Description: "Release mock", - }, - Chart: ch, - Config: &chart.Config{Raw: `name: "value"`}, - Version: version, - Namespace: namespace, - Hooks: []*release.Hook{ - { - Name: "pre-install-hook", - Kind: "Job", - Path: "pre-install-hook.yaml", - Manifest: mockHookTemplate, - LastRun: &date, - Events: []release.Hook_Event{release.Hook_PRE_INSTALL}, - }, - }, - Manifest: mockManifest, - } -} - // releaseCmd is a command that works with a FakeClient type releaseCmd func(c *helm.FakeClient, out io.Writer) *cobra.Command // runReleaseCases runs a set of release cases through the given releaseCmd. func runReleaseCases(t *testing.T, tests []releaseCase, rcmd releaseCmd) { + var buf bytes.Buffer for _, tt := range tests { c := &helm.FakeClient{ - Rels: []*release.Release{tt.resp}, + Rels: tt.rels, } cmd := rcmd(c, &buf) cmd.ParseFlags(tt.flags) @@ -154,6 +69,8 @@ type releaseCase struct { expected string err bool resp *release.Release + // Rels are the available releases at the start of the test. + rels []*release.Release } // tempHelmHome sets up a Helm Home in a temp dir. @@ -230,6 +147,7 @@ func ensureTestHome(home helmpath.Home, t *testing.T) error { t.Logf("$HELM_HOME has been configured at %s.\n", settings.Home.String()) return nil + } func TestRootCmd(t *testing.T) { diff --git a/cmd/helm/history_test.go b/cmd/helm/history_test.go index 3ff4b4a89..f193f6314 100644 --- a/cmd/helm/history_test.go +++ b/cmd/helm/history_test.go @@ -27,10 +27,10 @@ import ( func TestHistoryCmd(t *testing.T) { mk := func(name string, vers int32, code rpb.Status_Code) *rpb.Release { - return releaseMock(&releaseOptions{ - name: name, - version: vers, - statusCode: code, + return helm.ReleaseMock(&helm.MockReleaseOptions{ + Name: name, + Version: vers, + StatusCode: code, }) } diff --git a/cmd/helm/install_test.go b/cmd/helm/install_test.go index c68deb482..aa828c6ce 100644 --- a/cmd/helm/install_test.go +++ b/cmd/helm/install_test.go @@ -24,7 +24,6 @@ import ( "testing" "github.com/spf13/cobra" - "k8s.io/helm/pkg/helm" ) @@ -36,46 +35,46 @@ func TestInstall(t *testing.T) { args: []string{"testdata/testcharts/alpine"}, flags: strings.Split("--name aeneas", " "), expected: "aeneas", - resp: releaseMock(&releaseOptions{name: "aeneas"}), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"}), }, // Install, no hooks { name: "install without hooks", args: []string{"testdata/testcharts/alpine"}, flags: strings.Split("--name aeneas --no-hooks", " "), - expected: "juno", - resp: releaseMock(&releaseOptions{name: "juno"}), + expected: "aeneas", + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"}), }, // Install, values from cli { name: "install with values", args: []string{"testdata/testcharts/alpine"}, - flags: strings.Split("--set foo=bar", " "), - resp: releaseMock(&releaseOptions{name: "virgil"}), + flags: strings.Split("--name virgil --set foo=bar", " "), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "virgil"}), expected: "virgil", }, // Install, values from cli via multiple --set { name: "install with multiple values", args: []string{"testdata/testcharts/alpine"}, - flags: strings.Split("--set foo=bar", "--set bar=foo"), - resp: releaseMock(&releaseOptions{name: "virgil"}), + flags: strings.Split("--name virgil --set foo=bar --set bar=foo", " "), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "virgil"}), expected: "virgil", }, // Install, values from yaml { name: "install with values", args: []string{"testdata/testcharts/alpine"}, - flags: strings.Split("-f testdata/testcharts/alpine/extra_values.yaml", " "), - resp: releaseMock(&releaseOptions{name: "virgil"}), + flags: strings.Split("--name virgil -f testdata/testcharts/alpine/extra_values.yaml", " "), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "virgil"}), expected: "virgil", }, // Install, values from multiple yaml { name: "install with values", args: []string{"testdata/testcharts/alpine"}, - flags: strings.Split("-f testdata/testcharts/alpine/extra_values.yaml -f testdata/testcharts/alpine/more_values.yaml", " "), - resp: releaseMock(&releaseOptions{name: "virgil"}), + flags: strings.Split("--name virgil -f testdata/testcharts/alpine/extra_values.yaml -f testdata/testcharts/alpine/more_values.yaml", " "), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "virgil"}), expected: "virgil", }, // Install, no charts @@ -90,23 +89,23 @@ func TestInstall(t *testing.T) { args: []string{"testdata/testcharts/alpine"}, flags: strings.Split("--name aeneas --replace", " "), expected: "aeneas", - resp: releaseMock(&releaseOptions{name: "aeneas"}), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"}), }, // Install, with timeout { name: "install with a timeout", args: []string{"testdata/testcharts/alpine"}, - flags: strings.Split("--timeout 120", " "), + flags: strings.Split("--name foobar --timeout 120", " "), expected: "foobar", - resp: releaseMock(&releaseOptions{name: "foobar"}), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "foobar"}), }, // Install, with wait { name: "install with a wait", args: []string{"testdata/testcharts/alpine"}, - flags: strings.Split("--wait", " "), + flags: strings.Split("--name apollo --wait", " "), expected: "apollo", - resp: releaseMock(&releaseOptions{name: "apollo"}), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "apollo"}), }, // Install, using the name-template { @@ -114,7 +113,7 @@ func TestInstall(t *testing.T) { args: []string{"testdata/testcharts/alpine"}, flags: []string{"--name-template", "{{upper \"foobar\"}}"}, expected: "FOOBAR", - resp: releaseMock(&releaseOptions{name: "FOOBAR"}), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "FOOBAR"}), }, // Install, perform chart verification along the way. { diff --git a/cmd/helm/list_test.go b/cmd/helm/list_test.go index 5660eb26a..587a7c39c 100644 --- a/cmd/helm/list_test.go +++ b/cmd/helm/list_test.go @@ -36,7 +36,7 @@ func TestListCmd(t *testing.T) { { name: "with a release", resp: []*release.Release{ - releaseMock(&releaseOptions{name: "thomas-guide"}), + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide"}), }, expected: "thomas-guide", }, @@ -44,7 +44,7 @@ func TestListCmd(t *testing.T) { name: "list", args: []string{}, resp: []*release.Release{ - releaseMock(&releaseOptions{name: "atlas"}), + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas"}), }, expected: "NAME \tREVISION\tUPDATED \tSTATUS \tCHART \tNAMESPACE\natlas\t1 \t(.*)\tDEPLOYED\tfoo-0.1.0-beta.1\tdefault \n", }, @@ -52,8 +52,8 @@ func TestListCmd(t *testing.T) { name: "list, one deployed, one failed", args: []string{"-q"}, resp: []*release.Release{ - releaseMock(&releaseOptions{name: "thomas-guide", statusCode: release.Status_FAILED}), - releaseMock(&releaseOptions{name: "atlas-guide", statusCode: release.Status_DEPLOYED}), + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide", StatusCode: release.Status_FAILED}), + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", StatusCode: release.Status_DEPLOYED}), }, expected: "thomas-guide\natlas-guide", }, @@ -61,8 +61,8 @@ func TestListCmd(t *testing.T) { name: "with a release, multiple flags", args: []string{"--deleted", "--deployed", "--failed", "-q"}, resp: []*release.Release{ - releaseMock(&releaseOptions{name: "thomas-guide", statusCode: release.Status_DELETED}), - releaseMock(&releaseOptions{name: "atlas-guide", statusCode: release.Status_DEPLOYED}), + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide", StatusCode: release.Status_DELETED}), + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", StatusCode: release.Status_DEPLOYED}), }, // Note: We're really only testing that the flags parsed correctly. Which results are returned // depends on the backend. And until pkg/helm is done, we can't mock this. @@ -72,8 +72,8 @@ func TestListCmd(t *testing.T) { name: "with a release, multiple flags", args: []string{"--all", "-q"}, resp: []*release.Release{ - releaseMock(&releaseOptions{name: "thomas-guide", statusCode: release.Status_DELETED}), - releaseMock(&releaseOptions{name: "atlas-guide", statusCode: release.Status_DEPLOYED}), + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide", StatusCode: release.Status_DELETED}), + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", StatusCode: release.Status_DEPLOYED}), }, // See note on previous test. expected: "thomas-guide\natlas-guide", @@ -82,8 +82,8 @@ func TestListCmd(t *testing.T) { name: "with a release, multiple flags, deleting", args: []string{"--all", "-q"}, resp: []*release.Release{ - releaseMock(&releaseOptions{name: "thomas-guide", statusCode: release.Status_DELETING}), - releaseMock(&releaseOptions{name: "atlas-guide", statusCode: release.Status_DEPLOYED}), + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide", StatusCode: release.Status_DELETING}), + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", StatusCode: release.Status_DEPLOYED}), }, // See note on previous test. expected: "thomas-guide\natlas-guide", @@ -92,8 +92,8 @@ func TestListCmd(t *testing.T) { name: "namespace defined, multiple flags", args: []string{"--all", "-q", "--namespace test123"}, resp: []*release.Release{ - releaseMock(&releaseOptions{name: "thomas-guide", namespace: "test123"}), - releaseMock(&releaseOptions{name: "atlas-guide", namespace: "test321"}), + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide", Namespace: "test123"}), + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", Namespace: "test321"}), }, // See note on previous test. expected: "thomas-guide", @@ -102,8 +102,8 @@ func TestListCmd(t *testing.T) { name: "with a pending release, multiple flags", args: []string{"--all", "-q"}, resp: []*release.Release{ - releaseMock(&releaseOptions{name: "thomas-guide", statusCode: release.Status_PENDING_INSTALL}), - releaseMock(&releaseOptions{name: "atlas-guide", statusCode: release.Status_DEPLOYED}), + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide", StatusCode: release.Status_PENDING_INSTALL}), + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", StatusCode: release.Status_DEPLOYED}), }, expected: "thomas-guide\natlas-guide", }, @@ -111,10 +111,10 @@ func TestListCmd(t *testing.T) { name: "with a pending release, pending flag", args: []string{"--pending", "-q"}, resp: []*release.Release{ - releaseMock(&releaseOptions{name: "thomas-guide", statusCode: release.Status_PENDING_INSTALL}), - releaseMock(&releaseOptions{name: "wild-idea", statusCode: release.Status_PENDING_UPGRADE}), - releaseMock(&releaseOptions{name: "crazy-maps", statusCode: release.Status_PENDING_ROLLBACK}), - releaseMock(&releaseOptions{name: "atlas-guide", statusCode: release.Status_DEPLOYED}), + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide", StatusCode: release.Status_PENDING_INSTALL}), + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "wild-idea", StatusCode: release.Status_PENDING_UPGRADE}), + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "crazy-maps", StatusCode: release.Status_PENDING_ROLLBACK}), + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", StatusCode: release.Status_DEPLOYED}), }, expected: "thomas-guide\nwild-idea\ncrazy-maps", }, diff --git a/cmd/helm/reset_test.go b/cmd/helm/reset_test.go index d6febd132..b036e25c9 100644 --- a/cmd/helm/reset_test.go +++ b/cmd/helm/reset_test.go @@ -107,7 +107,7 @@ func TestReset_deployedReleases(t *testing.T) { var buf bytes.Buffer resp := []*release.Release{ - releaseMock(&releaseOptions{name: "atlas-guide", statusCode: release.Status_DEPLOYED}), + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", StatusCode: release.Status_DEPLOYED}), } c := &helm.FakeClient{ Rels: resp, @@ -139,7 +139,7 @@ func TestReset_forceFlag(t *testing.T) { var buf bytes.Buffer resp := []*release.Release{ - releaseMock(&releaseOptions{name: "atlas-guide", statusCode: release.Status_DEPLOYED}), + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", StatusCode: release.Status_DEPLOYED}), } c := &helm.FakeClient{ Rels: resp, diff --git a/cmd/helm/upgrade_test.go b/cmd/helm/upgrade_test.go index 1c5d1c856..187d3593e 100644 --- a/cmd/helm/upgrade_test.go +++ b/cmd/helm/upgrade_test.go @@ -28,6 +28,7 @@ import ( "k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/helm" "k8s.io/helm/pkg/proto/hapi/chart" + "k8s.io/helm/pkg/proto/hapi/release" ) func TestUpgradeCmd(t *testing.T) { @@ -43,9 +44,9 @@ func TestUpgradeCmd(t *testing.T) { t.Errorf("Error creating chart for upgrade: %v", err) } ch, _ := chartutil.Load(chartPath) - _ = releaseMock(&releaseOptions{ - name: "funny-bunny", - chart: ch, + _ = helm.ReleaseMock(&helm.MockReleaseOptions{ + Name: "funny-bunny", + Chart: ch, }) // update chart version @@ -94,61 +95,68 @@ func TestUpgradeCmd(t *testing.T) { { name: "upgrade a release", args: []string{"funny-bunny", chartPath}, - resp: releaseMock(&releaseOptions{name: "funny-bunny", version: 2, chart: ch}), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "funny-bunny", Version: 2, Chart: ch}), expected: "Release \"funny-bunny\" has been upgraded. Happy Helming!\n", + rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "funny-bunny", Version: 2, Chart: ch})}, }, { name: "upgrade a release with timeout", args: []string{"funny-bunny", chartPath}, flags: []string{"--timeout", "120"}, - resp: releaseMock(&releaseOptions{name: "funny-bunny", version: 3, chart: ch2}), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "funny-bunny", Version: 3, Chart: ch2}), expected: "Release \"funny-bunny\" has been upgraded. Happy Helming!\n", + rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "funny-bunny", Version: 3, Chart: ch2})}, }, { name: "upgrade a release with --reset-values", args: []string{"funny-bunny", chartPath}, flags: []string{"--reset-values", "true"}, - resp: releaseMock(&releaseOptions{name: "funny-bunny", version: 4, chart: ch2}), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "funny-bunny", Version: 4, Chart: ch2}), expected: "Release \"funny-bunny\" has been upgraded. Happy Helming!\n", + rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "funny-bunny", Version: 4, Chart: ch2})}, }, { name: "upgrade a release with --reuse-values", args: []string{"funny-bunny", chartPath}, flags: []string{"--reuse-values", "true"}, - resp: releaseMock(&releaseOptions{name: "funny-bunny", version: 5, chart: ch2}), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "funny-bunny", Version: 5, Chart: ch2}), expected: "Release \"funny-bunny\" has been upgraded. Happy Helming!\n", + rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "funny-bunny", Version: 5, Chart: ch2})}, }, { name: "install a release with 'upgrade --install'", args: []string{"zany-bunny", chartPath}, flags: []string{"-i"}, - resp: releaseMock(&releaseOptions{name: "zany-bunny", version: 1, chart: ch}), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "zany-bunny", Version: 1, Chart: ch}), expected: "Release \"zany-bunny\" has been upgraded. Happy Helming!\n", + rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "zany-bunny", Version: 1, Chart: ch})}, }, { name: "install a release with 'upgrade --install' and timeout", args: []string{"crazy-bunny", chartPath}, flags: []string{"-i", "--timeout", "120"}, - resp: releaseMock(&releaseOptions{name: "crazy-bunny", version: 1, chart: ch}), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "crazy-bunny", Version: 1, Chart: ch}), expected: "Release \"crazy-bunny\" has been upgraded. Happy Helming!\n", + rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "crazy-bunny", Version: 1, Chart: ch})}, }, { name: "upgrade a release with wait", args: []string{"crazy-bunny", chartPath}, flags: []string{"--wait"}, - resp: releaseMock(&releaseOptions{name: "crazy-bunny", version: 2, chart: ch2}), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "crazy-bunny", Version: 2, Chart: ch2}), expected: "Release \"crazy-bunny\" has been upgraded. Happy Helming!\n", + rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "crazy-bunny", Version: 2, Chart: ch2})}, }, { name: "upgrade a release with missing dependencies", args: []string{"bonkers-bunny", missingDepsPath}, - resp: releaseMock(&releaseOptions{name: "bonkers-bunny", version: 1, chart: ch3}), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "bonkers-bunny", Version: 1, Chart: ch3}), err: true, }, { name: "upgrade a release with bad dependencies", args: []string{"bonkers-bunny", badDepsPath}, - resp: releaseMock(&releaseOptions{name: "bonkers-bunny", version: 1, chart: ch3}), + resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "bonkers-bunny", Version: 1, Chart: ch3}), err: true, }, } diff --git a/pkg/helm/fake.go b/pkg/helm/fake.go index c66777673..8d809f658 100644 --- a/pkg/helm/fake.go +++ b/pkg/helm/fake.go @@ -17,9 +17,12 @@ limitations under the License. package helm // import "k8s.io/helm/pkg/helm" import ( + "errors" "fmt" + "math/rand" "sync" + "github.com/golang/protobuf/ptypes/timestamp" "k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/proto/hapi/release" rls "k8s.io/helm/pkg/proto/hapi/services" @@ -30,7 +33,15 @@ import ( type FakeClient struct { Rels []*release.Release Responses map[string]release.TestRun_Status - Err error + Opts options +} + +// Option returns the fake release client +func (c *FakeClient) Option(opts ...Option) Interface { + for _, opt := range opts { + opt(&c.Opts) + } + return c } var _ Interface = &FakeClient{} @@ -42,31 +53,49 @@ func (c *FakeClient) ListReleases(opts ...ReleaseListOption) (*rls.ListReleasesR Count: int64(len(c.Rels)), Releases: c.Rels, } - return resp, c.Err + return resp, nil } -// InstallRelease returns a response with the first Release on the fake release client +// InstallRelease creates a new release and returns a InstallReleaseResponse containing that release func (c *FakeClient) InstallRelease(chStr, ns string, opts ...InstallOption) (*rls.InstallReleaseResponse, error) { - return &rls.InstallReleaseResponse{ - Release: c.Rels[0], - }, nil + chart := &chart.Chart{} + return c.InstallReleaseFromChart(chart, ns, opts...) } -// InstallReleaseFromChart returns a response with the first Release on the fake release client +// InstallReleaseFromChart adds a new MockRelease to the fake client and returns a InstallReleaseResponse containing that release func (c *FakeClient) InstallReleaseFromChart(chart *chart.Chart, ns string, opts ...InstallOption) (*rls.InstallReleaseResponse, error) { + for _, opt := range opts { + opt(&c.Opts) + } + + releaseName := c.Opts.instReq.Name + + // Check to see if the release already exists. + rel, err := c.ReleaseStatus(releaseName, nil) + if err == nil && rel != nil { + return nil, errors.New("cannot re-use a name that is still in use") + } + + release := ReleaseMock(&MockReleaseOptions{Name: releaseName, Namespace: ns}) + c.Rels = append(c.Rels, release) + return &rls.InstallReleaseResponse{ - Release: c.Rels[0], + Release: release, }, nil } -// DeleteRelease returns nil, nil +// DeleteRelease deletes a release from the FakeClient func (c *FakeClient) DeleteRelease(rlsName string, opts ...DeleteOption) (*rls.UninstallReleaseResponse, error) { - return nil, nil -} + for i, rel := range c.Rels { + if rel.Name == rlsName { + c.Rels = append(c.Rels[:i], c.Rels[i+1:]...) + return &rls.UninstallReleaseResponse{ + Release: rel, + }, nil + } + } -// UpdateRelease returns nil, nil -func (c *FakeClient) UpdateRelease(rlsName string, chStr string, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error) { - return nil, nil + return nil, fmt.Errorf("No such release: %s", rlsName) } // GetVersion returns a fake version @@ -78,9 +107,20 @@ func (c *FakeClient) GetVersion(opts ...VersionOption) (*rls.GetVersionResponse, }, nil } -// UpdateReleaseFromChart returns nil, nil +// UpdateRelease returns an UpdateReleaseResponse containing the updated release, if it exists +func (c *FakeClient) UpdateRelease(rlsName string, chStr string, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error) { + return c.UpdateReleaseFromChart(rlsName, &chart.Chart{}, opts...) +} + +// UpdateReleaseFromChart returns an UpdateReleaseResponse containing the updated release, if it exists func (c *FakeClient) UpdateReleaseFromChart(rlsName string, chart *chart.Chart, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error) { - return nil, nil + // Check to see if the release already exists. + rel, err := c.ReleaseContent(rlsName, nil) + if err != nil { + return nil, err + } + + return &rls.UpdateReleaseResponse{Release: rel.Release}, nil } // RollbackRelease returns nil, nil @@ -88,32 +128,35 @@ func (c *FakeClient) RollbackRelease(rlsName string, opts ...RollbackOption) (*r return nil, nil } -// ReleaseStatus returns a release status response with info from the first release in the fake -// release client +// ReleaseStatus returns a release status response with info from the matching release name. func (c *FakeClient) ReleaseStatus(rlsName string, opts ...StatusOption) (*rls.GetReleaseStatusResponse, error) { - if c.Rels[0] != nil { - return &rls.GetReleaseStatusResponse{ - Name: c.Rels[0].Name, - Info: c.Rels[0].Info, - Namespace: c.Rels[0].Namespace, - }, nil + for _, rel := range c.Rels { + if rel.Name == rlsName { + return &rls.GetReleaseStatusResponse{ + Name: rel.Name, + Info: rel.Info, + Namespace: rel.Namespace, + }, nil + } } return nil, fmt.Errorf("No such release: %s", rlsName) } -// ReleaseContent returns the configuration for the first release in the fake release client +// ReleaseContent returns the configuration for the matching release name in the fake release client. func (c *FakeClient) ReleaseContent(rlsName string, opts ...ContentOption) (resp *rls.GetReleaseContentResponse, err error) { - if len(c.Rels) > 0 { - resp = &rls.GetReleaseContentResponse{ - Release: c.Rels[0], + for _, rel := range c.Rels { + if rel.Name == rlsName { + return &rls.GetReleaseContentResponse{ + Release: rel, + }, nil } } - return resp, c.Err + return resp, fmt.Errorf("No such release: %s", rlsName) } // ReleaseHistory returns a release's revision history. func (c *FakeClient) ReleaseHistory(rlsName string, opts ...HistoryOption) (*rls.GetHistoryResponse, error) { - return &rls.GetHistoryResponse{Releases: c.Rels}, c.Err + return &rls.GetHistoryResponse{Releases: c.Rels}, nil } // RunReleaseTest executes a pre-defined tests on a release @@ -141,7 +184,89 @@ func (c *FakeClient) RunReleaseTest(rlsName string, opts ...ReleaseTestOption) ( return results, errc } -// Option returns the fake release client -func (c *FakeClient) Option(opt ...Option) Interface { - return c +// MockHookTemplate is the hook template used for all mock release objects. +var MockHookTemplate = `apiVersion: v1 +kind: Job +metadata: + annotations: + "helm.sh/hooks": pre-install +` + +// MockManifest is the manifest used for all mock release objects. +var MockManifest = `apiVersion: v1 +kind: Secret +metadata: + name: fixture +` + +// MockReleaseOptions allows for user-configurable options on mock release objects. +type MockReleaseOptions struct { + Name string + Version int32 + Chart *chart.Chart + StatusCode release.Status_Code + Namespace string +} + +// ReleaseMock creates a mock release object based on options set by MockReleaseOptions. This function should typically not be used outside of testing. +func ReleaseMock(opts *MockReleaseOptions) *release.Release { + date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0} + + name := opts.Name + if name == "" { + name = "testrelease-" + string(rand.Intn(100)) + } + + var version int32 = 1 + if opts.Version != 0 { + version = opts.Version + } + + namespace := opts.Namespace + if namespace == "" { + namespace = "default" + } + + ch := opts.Chart + if opts.Chart == nil { + ch = &chart.Chart{ + Metadata: &chart.Metadata{ + Name: "foo", + Version: "0.1.0-beta.1", + }, + Templates: []*chart.Template{ + {Name: "templates/foo.tpl", Data: []byte(MockManifest)}, + }, + } + } + + scode := release.Status_DEPLOYED + if opts.StatusCode > 0 { + scode = opts.StatusCode + } + + return &release.Release{ + Name: name, + Info: &release.Info{ + FirstDeployed: &date, + LastDeployed: &date, + Status: &release.Status{Code: scode}, + Description: "Release mock", + }, + Chart: ch, + Config: &chart.Config{Raw: `name: "value"`}, + Version: version, + Namespace: namespace, + Hooks: []*release.Hook{ + { + Name: "pre-install-hook", + Kind: "Job", + Path: "pre-install-hook.yaml", + Manifest: MockHookTemplate, + LastRun: &date, + Events: []release.Hook_Event{release.Hook_PRE_INSTALL}, + }, + }, + Manifest: MockManifest, + } } diff --git a/pkg/helm/fake_test.go b/pkg/helm/fake_test.go new file mode 100644 index 000000000..9c0a53759 --- /dev/null +++ b/pkg/helm/fake_test.go @@ -0,0 +1,283 @@ +/* +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 helm + +import ( + "reflect" + "testing" + + "k8s.io/helm/pkg/proto/hapi/chart" + "k8s.io/helm/pkg/proto/hapi/release" + rls "k8s.io/helm/pkg/proto/hapi/services" +) + +func TestFakeClient_ReleaseStatus(t *testing.T) { + releasePresent := ReleaseMock(&MockReleaseOptions{Name: "release-present"}) + releaseNotPresent := ReleaseMock(&MockReleaseOptions{Name: "release-not-present"}) + + type fields struct { + Rels []*release.Release + } + type args struct { + rlsName string + opts []StatusOption + } + tests := []struct { + name string + fields fields + args args + want *rls.GetReleaseStatusResponse + wantErr bool + }{ + { + name: "Get a single release that exists", + fields: fields{ + Rels: []*release.Release{ + releasePresent, + }, + }, + args: args{ + rlsName: releasePresent.Name, + opts: nil, + }, + want: &rls.GetReleaseStatusResponse{ + Name: releasePresent.Name, + Info: releasePresent.Info, + Namespace: releasePresent.Namespace, + }, + + wantErr: false, + }, + { + name: "Get a release that does not exist", + fields: fields{ + Rels: []*release.Release{ + releasePresent, + }, + }, + args: args{ + rlsName: releaseNotPresent.Name, + opts: nil, + }, + want: nil, + wantErr: true, + }, + { + name: "Get a single release that exists from list", + fields: fields{ + Rels: []*release.Release{ + ReleaseMock(&MockReleaseOptions{Name: "angry-dolphin", Namespace: "default"}), + ReleaseMock(&MockReleaseOptions{Name: "trepid-tapir", Namespace: "default"}), + releasePresent, + }, + }, + args: args{ + rlsName: releasePresent.Name, + opts: nil, + }, + want: &rls.GetReleaseStatusResponse{ + Name: releasePresent.Name, + Info: releasePresent.Info, + Namespace: releasePresent.Namespace, + }, + + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &FakeClient{ + Rels: tt.fields.Rels, + } + got, err := c.ReleaseStatus(tt.args.rlsName, tt.args.opts...) + if (err != nil) != tt.wantErr { + t.Errorf("FakeClient.ReleaseStatus() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("FakeClient.ReleaseStatus() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestFakeClient_InstallReleaseFromChart(t *testing.T) { + installChart := &chart.Chart{} + type fields struct { + Rels []*release.Release + } + type args struct { + ns string + opts []InstallOption + } + tests := []struct { + name string + fields fields + args args + want *rls.InstallReleaseResponse + relsAfter []*release.Release + wantErr bool + }{ + { + name: "Add release to an empty list.", + fields: fields{ + Rels: []*release.Release{}, + }, + args: args{ + ns: "default", + opts: []InstallOption{ReleaseName("new-release")}, + }, + want: &rls.InstallReleaseResponse{ + Release: ReleaseMock(&MockReleaseOptions{Name: "new-release"}), + }, + relsAfter: []*release.Release{ + ReleaseMock(&MockReleaseOptions{Name: "new-release"}), + }, + wantErr: false, + }, + { + name: "Try to add a release where the name already exists.", + fields: fields{ + Rels: []*release.Release{ + ReleaseMock(&MockReleaseOptions{Name: "new-release"}), + }, + }, + args: args{ + ns: "default", + opts: []InstallOption{ReleaseName("new-release")}, + }, + relsAfter: []*release.Release{ + ReleaseMock(&MockReleaseOptions{Name: "new-release"}), + }, + want: nil, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &FakeClient{ + Rels: tt.fields.Rels, + } + got, err := c.InstallReleaseFromChart(installChart, tt.args.ns, tt.args.opts...) + if (err != nil) != tt.wantErr { + t.Errorf("FakeClient.InstallReleaseFromChart() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("FakeClient.InstallReleaseFromChart() = %v, want %v", got, tt.want) + } + if !reflect.DeepEqual(c.Rels, tt.relsAfter) { + t.Errorf("FakeClient.InstallReleaseFromChart() rels = %v, expected %v", got, tt.relsAfter) + } + }) + } +} + +func TestFakeClient_DeleteRelease(t *testing.T) { + type fields struct { + Rels []*release.Release + } + type args struct { + rlsName string + opts []DeleteOption + } + tests := []struct { + name string + fields fields + args args + want *rls.UninstallReleaseResponse + relsAfter []*release.Release + wantErr bool + }{ + { + name: "Delete a release that exists.", + fields: fields{ + Rels: []*release.Release{ + ReleaseMock(&MockReleaseOptions{Name: "angry-dolphin"}), + ReleaseMock(&MockReleaseOptions{Name: "trepid-tapir"}), + }, + }, + args: args{ + rlsName: "trepid-tapir", + opts: []DeleteOption{}, + }, + relsAfter: []*release.Release{ + ReleaseMock(&MockReleaseOptions{Name: "angry-dolphin"}), + }, + want: &rls.UninstallReleaseResponse{ + Release: ReleaseMock(&MockReleaseOptions{Name: "trepid-tapir"}), + }, + wantErr: false, + }, + { + name: "Delete a release that does not exist.", + fields: fields{ + Rels: []*release.Release{ + ReleaseMock(&MockReleaseOptions{Name: "angry-dolphin"}), + ReleaseMock(&MockReleaseOptions{Name: "trepid-tapir"}), + }, + }, + args: args{ + rlsName: "release-that-does-not-exists", + opts: []DeleteOption{}, + }, + relsAfter: []*release.Release{ + ReleaseMock(&MockReleaseOptions{Name: "angry-dolphin"}), + ReleaseMock(&MockReleaseOptions{Name: "trepid-tapir"}), + }, + want: nil, + wantErr: true, + }, + { + name: "Delete when only 1 item exists.", + fields: fields{ + Rels: []*release.Release{ + ReleaseMock(&MockReleaseOptions{Name: "trepid-tapir"}), + }, + }, + args: args{ + rlsName: "trepid-tapir", + opts: []DeleteOption{}, + }, + relsAfter: []*release.Release{}, + want: &rls.UninstallReleaseResponse{ + Release: ReleaseMock(&MockReleaseOptions{Name: "trepid-tapir"}), + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &FakeClient{ + Rels: tt.fields.Rels, + } + got, err := c.DeleteRelease(tt.args.rlsName, tt.args.opts...) + if (err != nil) != tt.wantErr { + t.Errorf("FakeClient.DeleteRelease() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("FakeClient.DeleteRelease() = %v, want %v", got, tt.want) + } + + if !reflect.DeepEqual(c.Rels, tt.relsAfter) { + t.Errorf("FakeClient.InstallReleaseFromChart() rels = %v, expected %v", got, tt.relsAfter) + } + }) + } +}