diff --git a/cmd/helm/install.go b/cmd/helm/install.go
index 1488b87c59d967d0ddb5533d32abdf6b4273c72c..1c2dca7efd6fa6c379b0148293286d8ddfc6ac70 100644
--- a/cmd/helm/install.go
+++ b/cmd/helm/install.go
@@ -37,8 +37,8 @@ import (
 	"k8s.io/helm/pkg/getter"
 	"k8s.io/helm/pkg/helm"
 	"k8s.io/helm/pkg/kube"
-	"k8s.io/helm/pkg/proto/hapi/chart"
 	"k8s.io/helm/pkg/proto/hapi/release"
+	"k8s.io/helm/pkg/renderutil"
 	"k8s.io/helm/pkg/repo"
 	"k8s.io/helm/pkg/strvals"
 )
@@ -254,7 +254,7 @@ func (i *installCmd) run() error {
 		// If checkDependencies returns an error, we have unfulfilled dependencies.
 		// As of Helm 2.4.0, this is treated as a stopping condition:
 		// https://github.com/kubernetes/helm/issues/2209
-		if err := checkDependencies(chartRequested, req); err != nil {
+		if err := renderutil.CheckDependencies(chartRequested, req); err != nil {
 			if i.depUp {
 				man := &downloader.Manager{
 					Out:        i.out,
@@ -515,29 +515,6 @@ func defaultNamespace() string {
 	return "default"
 }
 
-func checkDependencies(ch *chart.Chart, reqs *chartutil.Requirements) error {
-	missing := []string{}
-
-	deps := ch.GetDependencies()
-	for _, r := range reqs.Dependencies {
-		found := false
-		for _, d := range deps {
-			if d.Metadata.Name == r.Name {
-				found = true
-				break
-			}
-		}
-		if !found {
-			missing = append(missing, r.Name)
-		}
-	}
-
-	if len(missing) > 0 {
-		return fmt.Errorf("found in requirements.yaml, but missing in charts/ directory: %s", strings.Join(missing, ", "))
-	}
-	return nil
-}
-
 //readFile load a file from the local directory or a remote file with a url.
 func readFile(filePath, CertFile, KeyFile, CAFile string) ([]byte, error) {
 	u, _ := url.Parse(filePath)
diff --git a/cmd/helm/package.go b/cmd/helm/package.go
index 81603e67b1ef9e66916c76a8f61fd55145dea73e..51686dba7a70c6a5c301bb557fb4e33ece76884a 100644
--- a/cmd/helm/package.go
+++ b/cmd/helm/package.go
@@ -35,6 +35,7 @@ import (
 	"k8s.io/helm/pkg/helm/helmpath"
 	"k8s.io/helm/pkg/proto/hapi/chart"
 	"k8s.io/helm/pkg/provenance"
+	"k8s.io/helm/pkg/renderutil"
 	"k8s.io/helm/pkg/repo"
 )
 
@@ -151,7 +152,7 @@ func (p *packageCmd) run() error {
 	}
 
 	if reqs, err := chartutil.LoadRequirements(ch); err == nil {
-		if err := checkDependencies(ch, reqs); err != nil {
+		if err := renderutil.CheckDependencies(ch, reqs); err != nil {
 			return err
 		}
 	} else {
diff --git a/cmd/helm/template.go b/cmd/helm/template.go
index 54eb9ff4960f1959243a2da874d83265f86fadb4..63609c18c26b8687e54310557d89af90fc845962 100644
--- a/cmd/helm/template.go
+++ b/cmd/helm/template.go
@@ -27,17 +27,15 @@ import (
 	"strings"
 	"time"
 
-	"github.com/Masterminds/semver"
 	"github.com/spf13/cobra"
 
 	"k8s.io/helm/pkg/chartutil"
-	"k8s.io/helm/pkg/engine"
+	"k8s.io/helm/pkg/manifest"
 	"k8s.io/helm/pkg/proto/hapi/chart"
 	"k8s.io/helm/pkg/proto/hapi/release"
-	util "k8s.io/helm/pkg/releaseutil"
+	"k8s.io/helm/pkg/renderutil"
 	"k8s.io/helm/pkg/tiller"
 	"k8s.io/helm/pkg/timeconv"
-	tversion "k8s.io/helm/pkg/version"
 )
 
 const defaultDirectoryPermission = 0755
@@ -154,69 +152,21 @@ func (t *templateCmd) run(cmd *cobra.Command, args []string) error {
 		return prettyError(err)
 	}
 
-	if req, err := chartutil.LoadRequirements(c); err == nil {
-		if err := checkDependencies(c, req); err != nil {
-			return prettyError(err)
-		}
-	} else if err != chartutil.ErrRequirementsNotFound {
-		return fmt.Errorf("cannot load requirements: %v", err)
-	}
-	options := chartutil.ReleaseOptions{
-		Name:      t.releaseName,
-		IsInstall: !t.releaseIsUpgrade,
-		IsUpgrade: t.releaseIsUpgrade,
-		Time:      timeconv.Now(),
-		Namespace: t.namespace,
-	}
-
-	err = chartutil.ProcessRequirementsEnabled(c, config)
-	if err != nil {
-		return err
-	}
-	err = chartutil.ProcessRequirementsImportValues(c)
-	if err != nil {
-		return err
-	}
-
-	// Set up engine.
-	renderer := engine.New()
-
-	caps := &chartutil.Capabilities{
-		APIVersions:   chartutil.DefaultVersionSet,
-		KubeVersion:   chartutil.DefaultKubeVersion,
-		TillerVersion: tversion.GetVersionProto(),
-	}
-
-	// kubernetes version
-	kv, err := semver.NewVersion(t.kubeVersion)
-	if err != nil {
-		return fmt.Errorf("could not parse a kubernetes version: %v", err)
-	}
-	caps.KubeVersion.Major = fmt.Sprint(kv.Major())
-	caps.KubeVersion.Minor = fmt.Sprint(kv.Minor())
-	caps.KubeVersion.GitVersion = fmt.Sprintf("v%d.%d.0", kv.Major(), kv.Minor())
-
-	vals, err := chartutil.ToRenderValuesCaps(c, config, options, caps)
-	if err != nil {
-		return err
+	renderOpts := renderutil.Options{
+		ReleaseOptions: chartutil.ReleaseOptions{
+			Name:      t.releaseName,
+			IsInstall: !t.releaseIsUpgrade,
+			IsUpgrade: t.releaseIsUpgrade,
+			Time:      timeconv.Now(),
+			Namespace: t.namespace,
+		},
+		KubeVersion: t.kubeVersion,
 	}
 
-	out, err := renderer.Render(c, vals)
-	listManifests := []tiller.Manifest{}
+	renderedTemplates, err := renderutil.Render(c, config, renderOpts)
 	if err != nil {
 		return err
 	}
-	// extract kind and name
-	re := regexp.MustCompile("kind:(.*)\n")
-	for k, v := range out {
-		match := re.FindStringSubmatch(v)
-		h := "Unknown"
-		if len(match) == 2 {
-			h = strings.TrimSpace(match[1])
-		}
-		m := tiller.Manifest{Name: k, Content: v, Head: &util.SimpleHead{Kind: h}}
-		listManifests = append(listManifests, m)
-	}
 
 	if settings.Debug {
 		rel := &release.Release{
@@ -230,7 +180,8 @@ func (t *templateCmd) run(cmd *cobra.Command, args []string) error {
 		printRelease(os.Stdout, rel)
 	}
 
-	var manifestsToRender []tiller.Manifest
+	listManifests := manifest.SplitManifests(renderedTemplates)
+	var manifestsToRender []manifest.Manifest
 
 	// if we have a list of files to render, then check that each of the
 	// provided files exists in the chart.
diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go
index 23875289c7bce42a883a3b9629877945679c35ad..1eeafec038a30a52a78d5bfa6ea0ffdad6c34ce6 100644
--- a/cmd/helm/upgrade.go
+++ b/cmd/helm/upgrade.go
@@ -25,6 +25,7 @@ import (
 
 	"k8s.io/helm/pkg/chartutil"
 	"k8s.io/helm/pkg/helm"
+	"k8s.io/helm/pkg/renderutil"
 	"k8s.io/helm/pkg/storage/driver"
 )
 
@@ -238,7 +239,7 @@ func (u *upgradeCmd) run() error {
 	// Check chart requirements to make sure all dependencies are present in /charts
 	if ch, err := chartutil.Load(chartPath); err == nil {
 		if req, err := chartutil.LoadRequirements(ch); err == nil {
-			if err := checkDependencies(ch, req); err != nil {
+			if err := renderutil.CheckDependencies(ch, req); err != nil {
 				return err
 			}
 		} else if err != chartutil.ErrRequirementsNotFound {
diff --git a/pkg/helm/fake.go b/pkg/helm/fake.go
index a3b0ebc84defe220b5ef368582e43ced0d76c7cb..ffb5b40c9c41db1cac8c8f0e1d8aa4448cac3744 100644
--- a/pkg/helm/fake.go
+++ b/pkg/helm/fake.go
@@ -17,23 +17,29 @@ limitations under the License.
 package helm // import "k8s.io/helm/pkg/helm"
 
 import (
+	"bytes"
 	"errors"
 	"math/rand"
+	"strings"
 	"sync"
 
 	"github.com/golang/protobuf/ptypes/timestamp"
+	"k8s.io/helm/pkg/chartutil"
+	"k8s.io/helm/pkg/manifest"
 	"k8s.io/helm/pkg/proto/hapi/chart"
 	"k8s.io/helm/pkg/proto/hapi/release"
 	rls "k8s.io/helm/pkg/proto/hapi/services"
 	"k8s.io/helm/pkg/proto/hapi/version"
+	"k8s.io/helm/pkg/renderutil"
 	storage "k8s.io/helm/pkg/storage/driver"
 )
 
 // FakeClient implements Interface
 type FakeClient struct {
-	Rels      []*release.Release
-	Responses map[string]release.TestRun_Status
-	Opts      options
+	Rels            []*release.Release
+	Responses       map[string]release.TestRun_Status
+	Opts            options
+	RenderManifests bool
 }
 
 // Option returns the fake release client
@@ -96,7 +102,22 @@ func (c *FakeClient) InstallReleaseFromChart(chart *chart.Chart, ns string, opts
 		return nil, errors.New("cannot re-use a name that is still in use")
 	}
 
-	release := ReleaseMock(&MockReleaseOptions{Name: releaseName, Namespace: ns, Description: releaseDescription})
+	mockOpts := &MockReleaseOptions{
+		Name:        releaseName,
+		Chart:       chart,
+		Config:      c.Opts.instReq.Values,
+		Namespace:   ns,
+		Description: releaseDescription,
+	}
+
+	release := ReleaseMock(mockOpts)
+
+	if c.RenderManifests {
+		if err := RenderReleaseMock(release, false); err != nil {
+			return nil, err
+		}
+	}
+
 	if !c.Opts.dryRun {
 		c.Rels = append(c.Rels, release)
 	}
@@ -135,14 +156,44 @@ func (c *FakeClient) UpdateRelease(rlsName string, chStr string, opts ...UpdateO
 }
 
 // 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) {
+func (c *FakeClient) UpdateReleaseFromChart(rlsName string, newChart *chart.Chart, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error) {
+	for _, opt := range opts {
+		opt(&c.Opts)
+	}
 	// 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
+	mockOpts := &MockReleaseOptions{
+		Name:        rel.Release.Name,
+		Version:     rel.Release.Version + 1,
+		Chart:       newChart,
+		Config:      c.Opts.updateReq.Values,
+		Namespace:   rel.Release.Namespace,
+		Description: c.Opts.updateReq.Description,
+	}
+
+	newRelease := ReleaseMock(mockOpts)
+
+	if c.Opts.updateReq.ResetValues {
+		newRelease.Config = &chart.Config{Raw: "{}"}
+	} else if c.Opts.updateReq.ReuseValues {
+		// TODO: This should merge old and new values but does not.
+	}
+
+	if c.RenderManifests {
+		if err := RenderReleaseMock(newRelease, true); err != nil {
+			return nil, err
+		}
+	}
+
+	if !c.Opts.dryRun {
+		*rel.Release = *newRelease
+	}
+
+	return &rls.UpdateReleaseResponse{Release: newRelease}, nil
 }
 
 // RollbackRelease returns nil, nil
@@ -231,12 +282,15 @@ type MockReleaseOptions struct {
 	Name        string
 	Version     int32
 	Chart       *chart.Chart
+	Config      *chart.Config
 	StatusCode  release.Status_Code
 	Namespace   string
 	Description string
 }
 
-// ReleaseMock creates a mock release object based on options set by MockReleaseOptions. This function should typically not be used outside of testing.
+// 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}
 
@@ -273,6 +327,11 @@ func ReleaseMock(opts *MockReleaseOptions) *release.Release {
 		}
 	}
 
+	config := opts.Config
+	if config == nil {
+		config = &chart.Config{Raw: `name: "value"`}
+	}
+
 	scode := release.Status_DEPLOYED
 	if opts.StatusCode > 0 {
 		scode = opts.StatusCode
@@ -287,7 +346,7 @@ func ReleaseMock(opts *MockReleaseOptions) *release.Release {
 			Description:   description,
 		},
 		Chart:     ch,
-		Config:    &chart.Config{Raw: `name: "value"`},
+		Config:    config,
 		Version:   version,
 		Namespace: namespace,
 		Hooks: []*release.Hook{
@@ -303,3 +362,39 @@ func ReleaseMock(opts *MockReleaseOptions) *release.Release {
 		Manifest: MockManifest,
 	}
 }
+
+// RenderReleaseMock will take a release (usually produced by helm.ReleaseMock)
+// and will render the Manifest inside using the local mechanism (no tiller).
+// (Compare to renderResources in pkg/tiller)
+func RenderReleaseMock(r *release.Release, asUpgrade bool) error {
+	if r == nil || r.Chart == nil || r.Chart.Metadata == nil {
+		return errors.New("a release with a chart with metadata must be provided to render the manifests")
+	}
+
+	renderOpts := renderutil.Options{
+		ReleaseOptions: chartutil.ReleaseOptions{
+			Name:      r.Name,
+			Namespace: r.Namespace,
+			Time:      r.Info.LastDeployed,
+			Revision:  int(r.Version),
+			IsUpgrade: asUpgrade,
+			IsInstall: !asUpgrade,
+		},
+	}
+	rendered, err := renderutil.Render(r.Chart, r.Config, renderOpts)
+	if err != nil {
+		return err
+	}
+
+	b := bytes.NewBuffer(nil)
+	for _, m := range manifest.SplitManifests(rendered) {
+		// Remove empty manifests
+		if len(strings.TrimSpace(m.Content)) == 0 {
+			continue
+		}
+		b.WriteString("\n---\n# Source: " + m.Name + "\n")
+		b.WriteString(m.Content)
+	}
+	r.Manifest = b.String()
+	return nil
+}
diff --git a/pkg/helm/fake_test.go b/pkg/helm/fake_test.go
index f16fbaf4c2d393680b39742f0247ad15b459e3a5..ecb0a28555f9afe3b8574831c828af0d752a34d0 100644
--- a/pkg/helm/fake_test.go
+++ b/pkg/helm/fake_test.go
@@ -17,6 +17,7 @@ limitations under the License.
 package helm
 
 import (
+	"fmt"
 	"reflect"
 	"testing"
 
@@ -25,6 +26,57 @@ import (
 	rls "k8s.io/helm/pkg/proto/hapi/services"
 )
 
+const cmInputTemplate = `kind: ConfigMap
+apiVersion: v1
+metadata:
+  name: example
+data:
+  Release:
+{{.Release | toYaml | indent 4}}
+`
+const cmOutputTemplate = `
+---
+# Source: installChart/templates/cm.yaml
+kind: ConfigMap
+apiVersion: v1
+metadata:
+  name: example
+data:
+  Release:
+    IsInstall: %t
+    IsUpgrade: %t
+    Name: new-release
+    Namespace: default
+    Revision: %d
+    Service: Tiller
+    Time:
+      seconds: 242085845
+    
+`
+
+var installChart *chart.Chart
+
+func init() {
+	installChart = &chart.Chart{
+		Metadata: &chart.Metadata{Name: "installChart"},
+		Templates: []*chart.Template{
+			{Name: "templates/cm.yaml", Data: []byte(cmInputTemplate)},
+		},
+	}
+}
+
+func releaseWithChart(opts *MockReleaseOptions) *release.Release {
+	if opts.Chart == nil {
+		opts.Chart = installChart
+	}
+	return ReleaseMock(opts)
+}
+
+func withManifest(r *release.Release, isUpgrade bool) *release.Release {
+	r.Manifest = fmt.Sprintf(cmOutputTemplate, !isUpgrade, isUpgrade, r.Version)
+	return r
+}
+
 func TestFakeClient_ReleaseStatus(t *testing.T) {
 	releasePresent := ReleaseMock(&MockReleaseOptions{Name: "release-present"})
 	releaseNotPresent := ReleaseMock(&MockReleaseOptions{Name: "release-not-present"})
@@ -117,9 +169,9 @@ func TestFakeClient_ReleaseStatus(t *testing.T) {
 }
 
 func TestFakeClient_InstallReleaseFromChart(t *testing.T) {
-	installChart := &chart.Chart{}
 	type fields struct {
-		Rels []*release.Release
+		Rels            []*release.Release
+		RenderManifests bool
 	}
 	type args struct {
 		ns   string
@@ -143,10 +195,10 @@ func TestFakeClient_InstallReleaseFromChart(t *testing.T) {
 				opts: []InstallOption{ReleaseName("new-release")},
 			},
 			want: &rls.InstallReleaseResponse{
-				Release: ReleaseMock(&MockReleaseOptions{Name: "new-release"}),
+				Release: releaseWithChart(&MockReleaseOptions{Name: "new-release"}),
 			},
 			relsAfter: []*release.Release{
-				ReleaseMock(&MockReleaseOptions{Name: "new-release"}),
+				releaseWithChart(&MockReleaseOptions{Name: "new-release"}),
 			},
 			wantErr: false,
 		},
@@ -160,10 +212,10 @@ func TestFakeClient_InstallReleaseFromChart(t *testing.T) {
 				opts: []InstallOption{ReleaseName("new-release"), InstallDescription("foo-bar")},
 			},
 			want: &rls.InstallReleaseResponse{
-				Release: ReleaseMock(&MockReleaseOptions{Name: "new-release", Description: "foo-bar"}),
+				Release: releaseWithChart(&MockReleaseOptions{Name: "new-release", Description: "foo-bar"}),
 			},
 			relsAfter: []*release.Release{
-				ReleaseMock(&MockReleaseOptions{Name: "new-release", Description: "foo-bar"}),
+				releaseWithChart(&MockReleaseOptions{Name: "new-release", Description: "foo-bar"}),
 			},
 			wantErr: false,
 		},
@@ -171,7 +223,7 @@ func TestFakeClient_InstallReleaseFromChart(t *testing.T) {
 			name: "Try to add a release where the name already exists.",
 			fields: fields{
 				Rels: []*release.Release{
-					ReleaseMock(&MockReleaseOptions{Name: "new-release"}),
+					releaseWithChart(&MockReleaseOptions{Name: "new-release"}),
 				},
 			},
 			args: args{
@@ -179,16 +231,35 @@ func TestFakeClient_InstallReleaseFromChart(t *testing.T) {
 				opts: []InstallOption{ReleaseName("new-release")},
 			},
 			relsAfter: []*release.Release{
-				ReleaseMock(&MockReleaseOptions{Name: "new-release"}),
+				releaseWithChart(&MockReleaseOptions{Name: "new-release"}),
 			},
 			want:    nil,
 			wantErr: true,
 		},
+		{
+			name: "Render the given chart.",
+			fields: fields{
+				Rels:            []*release.Release{},
+				RenderManifests: true,
+			},
+			args: args{
+				ns:   "default",
+				opts: []InstallOption{ReleaseName("new-release")},
+			},
+			want: &rls.InstallReleaseResponse{
+				Release: withManifest(releaseWithChart(&MockReleaseOptions{Name: "new-release"}), false),
+			},
+			relsAfter: []*release.Release{
+				withManifest(releaseWithChart(&MockReleaseOptions{Name: "new-release"}), false),
+			},
+			wantErr: false,
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			c := &FakeClient{
-				Rels: tt.fields.Rels,
+				Rels:            tt.fields.Rels,
+				RenderManifests: tt.fields.RenderManifests,
 			}
 			got, err := c.InstallReleaseFromChart(installChart, tt.args.ns, tt.args.opts...)
 			if (err != nil) != tt.wantErr {
@@ -293,7 +364,84 @@ func TestFakeClient_DeleteRelease(t *testing.T) {
 			}
 
 			if !reflect.DeepEqual(c.Rels, tt.relsAfter) {
-				t.Errorf("FakeClient.InstallReleaseFromChart() rels = %v, expected %v", got, tt.relsAfter)
+				t.Errorf("FakeClient.InstallReleaseFromChart() rels = %v, expected %v", c.Rels, tt.relsAfter)
+			}
+		})
+	}
+}
+
+func TestFakeClient_UpdateReleaseFromChart(t *testing.T) {
+	type fields struct {
+		Rels            []*release.Release
+		RenderManifests bool
+	}
+	type args struct {
+		release string
+		opts    []UpdateOption
+	}
+	tests := []struct {
+		name      string
+		fields    fields
+		args      args
+		want      *rls.UpdateReleaseResponse
+		relsAfter []*release.Release
+		wantErr   bool
+	}{
+		{
+			name: "Update release.",
+			fields: fields{
+				Rels: []*release.Release{
+					releaseWithChart(&MockReleaseOptions{Name: "new-release"}),
+				},
+			},
+			args: args{
+				release: "new-release",
+				opts:    []UpdateOption{},
+			},
+			want: &rls.UpdateReleaseResponse{
+				Release: releaseWithChart(&MockReleaseOptions{Name: "new-release", Version: 2}),
+			},
+			relsAfter: []*release.Release{
+				releaseWithChart(&MockReleaseOptions{Name: "new-release", Version: 2}),
+			},
+		},
+		{
+			name: "Update and render given chart.",
+			fields: fields{
+				Rels: []*release.Release{
+					releaseWithChart(&MockReleaseOptions{Name: "new-release"}),
+				},
+				RenderManifests: true,
+			},
+			args: args{
+				release: "new-release",
+				opts:    []UpdateOption{},
+			},
+			want: &rls.UpdateReleaseResponse{
+				Release: withManifest(releaseWithChart(&MockReleaseOptions{Name: "new-release", Version: 2}), true),
+			},
+			relsAfter: []*release.Release{
+				withManifest(releaseWithChart(&MockReleaseOptions{Name: "new-release", Version: 2}), true),
+			},
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			c := &FakeClient{
+				Rels:            tt.fields.Rels,
+				RenderManifests: tt.fields.RenderManifests,
+			}
+			got, err := c.UpdateReleaseFromChart(tt.args.release, installChart, tt.args.opts...)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("FakeClient.UpdateReleaseFromChart() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("FakeClient.UpdateReleaseFromChart() =\n%v\nwant\n%v", got, tt.want)
+			}
+			if !reflect.DeepEqual(c.Rels, tt.relsAfter) {
+				t.Errorf("FakeClient.UpdateReleaseFromChart() rels =\n%v\nwant\n%v", c.Rels, tt.relsAfter)
 			}
 		})
 	}
diff --git a/pkg/manifest/doc.go b/pkg/manifest/doc.go
new file mode 100644
index 0000000000000000000000000000000000000000..c2f127cdaff189d407740ee61c8aa48af5e67e15
--- /dev/null
+++ b/pkg/manifest/doc.go
@@ -0,0 +1,23 @@
+/*
+Copyright The Helm Authors.
+
+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 manifest contains tools for working with kubernetes manifests.
+
+Much like other parts of helm, it does not generally require that the manifests
+be correct yaml, so these functions can be run on broken manifests to aid in
+user debugging
+*/
+package manifest // import "k8s.io/helm/pkg/manifest"
diff --git a/pkg/manifest/splitter.go b/pkg/manifest/splitter.go
new file mode 100644
index 0000000000000000000000000000000000000000..7081e7aa7b3f222c5cb564036b16f5383209746a
--- /dev/null
+++ b/pkg/manifest/splitter.go
@@ -0,0 +1,46 @@
+/*
+Copyright The Helm Authors.
+
+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 manifest
+
+import (
+	"regexp"
+	"strings"
+
+	"k8s.io/helm/pkg/releaseutil"
+)
+
+var (
+	kindRegex = regexp.MustCompile("kind:(.*)\n")
+)
+
+// SplitManifests takes a map of rendered templates and splits them into the
+// detected manifests.
+func SplitManifests(templates map[string]string) []Manifest {
+	var listManifests []Manifest
+	// extract kind and name
+	for k, v := range templates {
+		match := kindRegex.FindStringSubmatch(v)
+		h := "Unknown"
+		if len(match) == 2 {
+			h = strings.TrimSpace(match[1])
+		}
+		m := Manifest{Name: k, Content: v, Head: &releaseutil.SimpleHead{Kind: h}}
+		listManifests = append(listManifests, m)
+	}
+
+	return listManifests
+}
diff --git a/pkg/manifest/types.go b/pkg/manifest/types.go
new file mode 100644
index 0000000000000000000000000000000000000000..4c748c9e58876f6aaf6f4ed9834b6efbc1c1ea3c
--- /dev/null
+++ b/pkg/manifest/types.go
@@ -0,0 +1,28 @@
+/*
+Copyright The Helm Authors.
+
+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 manifest
+
+import (
+	"k8s.io/helm/pkg/releaseutil"
+)
+
+// Manifest represents a manifest file, which has a name and some content.
+type Manifest struct {
+	Name    string
+	Content string
+	Head    *releaseutil.SimpleHead
+}
diff --git a/pkg/renderutil/deps.go b/pkg/renderutil/deps.go
new file mode 100644
index 0000000000000000000000000000000000000000..72e4d12a15c31f58a440e76a5821f944a8bd4050
--- /dev/null
+++ b/pkg/renderutil/deps.go
@@ -0,0 +1,50 @@
+/*
+Copyright The Helm Authors.
+
+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 renderutil
+
+import (
+	"fmt"
+	"strings"
+
+	"k8s.io/helm/pkg/chartutil"
+	"k8s.io/helm/pkg/proto/hapi/chart"
+)
+
+// CheckDependencies will do a simple dependency check on the chart for local
+// rendering
+func CheckDependencies(ch *chart.Chart, reqs *chartutil.Requirements) error {
+	missing := []string{}
+
+	deps := ch.GetDependencies()
+	for _, r := range reqs.Dependencies {
+		found := false
+		for _, d := range deps {
+			if d.Metadata.Name == r.Name {
+				found = true
+				break
+			}
+		}
+		if !found {
+			missing = append(missing, r.Name)
+		}
+	}
+
+	if len(missing) > 0 {
+		return fmt.Errorf("found in requirements.yaml, but missing in charts/ directory: %s", strings.Join(missing, ", "))
+	}
+	return nil
+}
diff --git a/pkg/renderutil/doc.go b/pkg/renderutil/doc.go
new file mode 100644
index 0000000000000000000000000000000000000000..38c3ae60d23a7d59df3cc8e36388b04bc888b8d5
--- /dev/null
+++ b/pkg/renderutil/doc.go
@@ -0,0 +1,24 @@
+/*
+Copyright The Helm Authors.
+
+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 renderutil contains tools related to the local rendering of charts.
+
+Local rendering means rendering without the tiller; this is generally used for
+local debugging and testing (see the `helm template` command for examples of
+use).  This package will not render charts exactly the same way as the tiller
+will, but will be generally close enough for local debug purposes.
+*/
+package renderutil // import "k8s.io/helm/pkg/renderutil"
diff --git a/pkg/renderutil/render.go b/pkg/renderutil/render.go
new file mode 100644
index 0000000000000000000000000000000000000000..1996e1dc29722b56a9d4da54d96ddc60f5bd7423
--- /dev/null
+++ b/pkg/renderutil/render.go
@@ -0,0 +1,88 @@
+/*
+Copyright The Helm Authors.
+
+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 renderutil
+
+import (
+	"fmt"
+
+	"github.com/Masterminds/semver"
+
+	"k8s.io/helm/pkg/chartutil"
+	"k8s.io/helm/pkg/engine"
+	"k8s.io/helm/pkg/proto/hapi/chart"
+	tversion "k8s.io/helm/pkg/version"
+)
+
+// Options are options for this simple local render
+type Options struct {
+	ReleaseOptions chartutil.ReleaseOptions
+	KubeVersion    string
+}
+
+// Render chart templates locally and display the output.
+// This does not require the Tiller. Any values that would normally be
+// looked up or retrieved in-cluster will be faked locally. Additionally, none
+// of the server-side testing of chart validity (e.g. whether an API is supported)
+// is done.
+//
+// Note: a `nil` config passed here means "ignore the chart's default values";
+// if you want the normal behavior of merging the defaults with the new config,
+// you should pass `&chart.Config{Raw: "{}"},
+func Render(c *chart.Chart, config *chart.Config, opts Options) (map[string]string, error) {
+	if req, err := chartutil.LoadRequirements(c); err == nil {
+		if err := CheckDependencies(c, req); err != nil {
+			return nil, err
+		}
+	} else if err != chartutil.ErrRequirementsNotFound {
+		return nil, fmt.Errorf("cannot load requirements: %v", err)
+	}
+
+	err := chartutil.ProcessRequirementsEnabled(c, config)
+	if err != nil {
+		return nil, err
+	}
+	err = chartutil.ProcessRequirementsImportValues(c)
+	if err != nil {
+		return nil, err
+	}
+
+	// Set up engine.
+	renderer := engine.New()
+
+	caps := &chartutil.Capabilities{
+		APIVersions:   chartutil.DefaultVersionSet,
+		KubeVersion:   chartutil.DefaultKubeVersion,
+		TillerVersion: tversion.GetVersionProto(),
+	}
+
+	if opts.KubeVersion != "" {
+		kv, verErr := semver.NewVersion(opts.KubeVersion)
+		if verErr != nil {
+			return nil, fmt.Errorf("could not parse a kubernetes version: %v", verErr)
+		}
+		caps.KubeVersion.Major = fmt.Sprint(kv.Major())
+		caps.KubeVersion.Minor = fmt.Sprint(kv.Minor())
+		caps.KubeVersion.GitVersion = fmt.Sprintf("v%d.%d.0", kv.Major(), kv.Minor())
+	}
+
+	vals, err := chartutil.ToRenderValuesCaps(c, config, opts.ReleaseOptions, caps)
+	if err != nil {
+		return nil, err
+	}
+
+	return renderer.Render(c, vals)
+}
diff --git a/pkg/renderutil/render_test.go b/pkg/renderutil/render_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..e10ff883c945ab1031cdf1dfae49553cfa2eb7c7
--- /dev/null
+++ b/pkg/renderutil/render_test.go
@@ -0,0 +1,153 @@
+/*
+Copyright The Helm Authors.
+
+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 renderutil
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+
+	"k8s.io/helm/pkg/chartutil"
+	"k8s.io/helm/pkg/proto/hapi/chart"
+)
+
+const cmTemplate = `kind: ConfigMap
+apiVersion: v1
+metadata:
+  name: example
+data:
+  Chart:
+{{.Chart | toYaml | indent 4}}
+  Release:
+{{.Release | toYaml | indent 4}}
+  Values:
+{{.Values | toYaml | indent 4}}
+`
+
+func TestRender(t *testing.T) {
+
+	testChart := &chart.Chart{
+		Metadata: &chart.Metadata{Name: "hello"},
+		Templates: []*chart.Template{
+			{Name: "templates/cm.yaml", Data: []byte(cmTemplate)},
+		},
+		Values: &chart.Config{Raw: "meow: defaultmeow"},
+	}
+
+	newConfig := &chart.Config{Raw: "meow: newmeow"}
+	defaultConfig := &chart.Config{Raw: "{}"}
+
+	tests := map[string]struct {
+		chart  *chart.Chart
+		config *chart.Config
+		opts   Options
+		want   map[string]string
+	}{
+		"BasicWithValues": {
+			chart:  testChart,
+			config: newConfig,
+			opts:   Options{},
+			want: map[string]string{
+				"hello/templates/cm.yaml": `kind: ConfigMap
+apiVersion: v1
+metadata:
+  name: example
+data:
+  Chart:
+    name: hello
+    
+  Release:
+    IsInstall: false
+    IsUpgrade: false
+    Name: ""
+    Namespace: ""
+    Revision: 0
+    Service: Tiller
+    Time: null
+    
+  Values:
+    meow: newmeow
+    
+`,
+			},
+		},
+		"BasicNoValues": {
+			chart:  testChart,
+			config: defaultConfig,
+			opts:   Options{},
+			want: map[string]string{
+				"hello/templates/cm.yaml": `kind: ConfigMap
+apiVersion: v1
+metadata:
+  name: example
+data:
+  Chart:
+    name: hello
+    
+  Release:
+    IsInstall: false
+    IsUpgrade: false
+    Name: ""
+    Namespace: ""
+    Revision: 0
+    Service: Tiller
+    Time: null
+    
+  Values:
+    meow: defaultmeow
+    
+`,
+			},
+		},
+		"SetSomeReleaseValues": {
+			chart:  testChart,
+			config: defaultConfig,
+			opts:   Options{ReleaseOptions: chartutil.ReleaseOptions{Name: "meow"}},
+			want: map[string]string{
+				"hello/templates/cm.yaml": `kind: ConfigMap
+apiVersion: v1
+metadata:
+  name: example
+data:
+  Chart:
+    name: hello
+    
+  Release:
+    IsInstall: false
+    IsUpgrade: false
+    Name: meow
+    Namespace: ""
+    Revision: 0
+    Service: Tiller
+    Time: null
+    
+  Values:
+    meow: defaultmeow
+    
+`,
+			},
+		},
+	}
+
+	for testName, tt := range tests {
+		t.Run(testName, func(t *testing.T) {
+			got, err := Render(tt.chart, tt.config, tt.opts)
+			require.NoError(t, err)
+			require.Equal(t, tt.want, got)
+		})
+	}
+}
diff --git a/pkg/tiller/hooks.go b/pkg/tiller/hooks.go
index d53ed504f0a6624a9fefbe01749f5a8723d3295a..6c3543f61f02465ed7a6e2afcf127b8721aee1ea 100644
--- a/pkg/tiller/hooks.go
+++ b/pkg/tiller/hooks.go
@@ -27,6 +27,7 @@ import (
 
 	"k8s.io/helm/pkg/chartutil"
 	"k8s.io/helm/pkg/hooks"
+	"k8s.io/helm/pkg/manifest"
 	"k8s.io/helm/pkg/proto/hapi/release"
 	util "k8s.io/helm/pkg/releaseutil"
 )
@@ -52,12 +53,7 @@ var deletePolices = map[string]release.Hook_DeletePolicy{
 	hooks.BeforeHookCreation: release.Hook_BEFORE_HOOK_CREATION,
 }
 
-// Manifest represents a manifest file, which has a name and some content.
-type Manifest struct {
-	Name    string
-	Content string
-	Head    *util.SimpleHead
-}
+type Manifest = manifest.Manifest
 
 type result struct {
 	hooks   []*release.Hook