Unverified Commit e93b7716 authored by Adam Reese's avatar Adam Reese Committed by GitHub

Merge pull request #3025 from beeradb/fakeclient-releasestatus

Improvements to FakeClient: support all CRUD operations
parents 7c66074b ab049ddb
......@@ -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",
......
......@@ -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",
......
......@@ -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",
......
......@@ -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",
......
......@@ -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",
......
......@@ -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) {
......
......@@ -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,
})
}
......
......@@ -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.
{
......
......@@ -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",
},
......
......@@ -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,
......
......@@ -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,
},
}
......
......@@ -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,
}
}
/*
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)
}
})
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment